Статья о разработчиках, чья работа пришлась на эпоху до расцвета фреймворков. О том, как они держали индустрию на дисциплине, прямолинейности и технической смелости, и почему некоторые их подходы стоит возвращать в современную практику, хотя бы частично.

Иногда я ловлю себя на странном ощущении: будто многие современные инструменты исполняют роль такого мягкого пуха, через который мы перестали чувствовать остроту реального программирования. И тут я не о ностальгии, а скорее о том, что те, кто начинал путь в девяностых или в начале нул��вых, делали свою работу в куда более жестком окружении. Без фреймворков, без бесконечных библиотек, без удобных механизмов вроде ленивых генераторов и автоматических сборщиков. Даже если вы сами не пережили ту эпоху, наверняка хотя бы слышали истории, как кто-то руками поднимал сетевой стек или писал собственный шаблонизатор, просто потому что другого варианта не было.

Эта статья — попытка вспомнить те подходы, которые формировались в условиях почти аскетической разработки. И да, я знаю, что мы все давно живем в другом времени. Но есть такие вещи, которые переживают эпохи. Иногда мне кажется, что забытые практики разработчиков девяностых и нулевых могут дать современному программисту куда больше, чем кажется на первый взгляд.

Разработка без подстраховки: почему отсутствие фреймворков делало код чище

Если задуматься, многие современные фреймворки решают не проблему, а последствия того, что мы привыкли расслабляться. Когда нет готовой схемы, приходится не только думать, но и понимать, почему проект работает именно так. Программисты той эпохи были вынуждены каждый модуль собирать вручную, и в этом была одна важная деталь: структура проекта не диктовалась модой или готовым каркасом, а логикой конкретного решения.

Я помню, как мне попадались архивные фрагменты кода на C, где разработчик вручную проектировал обработку строки запроса. И знаете, в этом было что-то правильное. Никаких магических прослоек, никаких скрытых операций. Только сам код и знания человека, который его писал. Вот примерно такой стиль встречался довольно часто:

#include <stdio.h>
#include <string.h>

void parse(char* query) {
    char key[256];
    char value[256];

    int i = 0, j = 0, reading_value = 0;

    while (*query) {
        if (*query == '=') {
            key[j] = '\0';
            j = 0;
            reading_value = 1;
        } else if (*query == '&') {
            value[j] = '\0';
            printf("pair %s = %s\n", key, value);
            j = 0;
            reading_value = 0;
        } else {
            if (reading_value)
                value[j++] = *query;
            else
                key[j++] = *query;
        }
        query++;
    }
    value[j] = '\0';
    printf("pair %s = %s\n", key, value);
}

int main() {
    char query[] = "name=ivan&lang=c";
    parse(query);
    return 0;
}

Такой код сегодня назвали бы слишком приземленным. Но когда читаешь его, сразу появляется уважение к тому, как системно и аккуратно люди подходили к задаче. И да, мне интересно услышать мнение читателя: как часто вы сегодня встречаете инженера, который способен без страха разобрать подобный механизм вручную?

Дисциплина и экономия ресурса: оптимизация была не плюсом, а необходимостью

Сейчас оптимизация часто звучит как этап, который можно отложить. Но в конце девяностых и начале нулевых оптимизация была частью разработки. Просто потому что железо не давало права на ошибку. Оперативной памяти могло не хватить даже на банальную структуру, а время процессора измерялось настолько экономно, что любой лишний цикл бросался в глаза.

Тогда приходилось писать код так, чтобы он проходил путь от идеи до реального исполнения почти без отходов. И это замечательно тренировало мышление. Был даже устойчивый подход: если можешь сделать задачу за линейное время, делай. Если не можешь, придумай как. Нельзя сказать, что этот стиль полностью исчез. Но он стал скорее редкостью.

Вот пример кода на Pascal, который я когда-то нашел в старой подборке. Он решал задачу поиска самоповторов в массиве без дополнительной памяти. В современном мире мы бы использовали хэш, дерево, любой контейнер. Тогда же это был почти спортивный подвиг:

program FindDuplicates;

var
  arr: array[1..10] of integer;
  i, j: integer;

begin
  arr[1] := 3; arr[2] := 7; arr[3] := 3; arr[4] := 2;
  arr[5] := 9; arr[6] := 7; arr[7] := 1; arr[8] := 2;
  arr[9] := 0; arr[10] := 9;

  for i := 1 to 10 do begin
    for j := i + 1 to 10 do begin
      if arr[i] = arr[j] then
        writeln(arr[i]);
    end;
  end;
end.

Да, сегодня это выглядит грубо. Но в те времена это было нормой. И эта норма формировала привычку думать о том, что происходит в памяти, что делают циклы, как будет вести себя программа в реальном окружении. Не уверен, что подобные ограничения нужно возвращать, но определенно стоит иногда включать голову в том же направлении.

Работа ближе к железу: понимание реального мира машин

Еще одна важная особенность той эпохи — близость к железу. Программисты девяностых и нулевых часто взаимодействовали с устройствами напрямую, не через высокоуровневые библиотеки. И это давало то самое ощущение реальности, которого порой не хватает сегодня. Ты не просто пишешь код, ты понимаешь, как он живет внутри машины.

Возьмите любой пример программирования под ранние версии Windows или Linux. Даже простая работа с файлами требовала внимания. Нужно было думать, что произойдет, если буфер не заполнится, как себя поведет дескриптор, что будет, если операция прервется посередине. Вот пример кода на C под Linux, который демонстрирует ручное управление файловым вводом и выводом:

#include <unistd.h>
#include <fcntl.h>

int main() {
    int fd = open("data.txt", O_RDONLY);
    if (fd < 0) return 1;

    char buf[64];
    int n;

    while ((n = read(fd, buf, 64)) > 0) {
        write(1, buf, n);
    }

    close(fd);
    return 0;
}

Вроде бы простая вещь. Но она заставляет думать: что мы читаем, сколько читаем, где храним, как передаем дальше. Да, сейчас все это прячется за красивыми обёртками. Но разве не полезно иногда вспомнить, что под ними существует реальный процесс?

Именно это понимание помогало программистам той эпохи тащить индустрию на себе. Они знали, что происходит под капотом. А значит, могли решать задачи, которые сегодня часто пугают разработчиков.

Системное мышление, которое стоит вернуть: ручная сборка решений

Самое главное качество той эпохи — умение не искать обходные пути, а собирать решение целиком. Когда нет фреймворков, хочешь не хочешь начинаешь думать в терминах архитектуры, а не готового шаблона. Это напоминает ситуацию, когда человек учится играть на музыкальном инструменте: сначала больно, потом сложно, а потом вдруг начинаешь чувствовать форму.

Многие практики, родившиеся в тех условиях, до сих пор актуальны. Это ручная декомпозиция, внимательное отношение к данным, осознанная работа со сложностью. В конце концов, простая структура проекта — это не дань моде, а способ сохранять ясность мыслей.

Вот пример проекта на Python тех лет. Да, это просто игрушечный веб сервер, написанный вручную. Но он отлично показывает мысль: автор понимал весь путь запроса от начала до конца. И это делает разработчика сильнее:

import socket

def handle(client):
    data = client.recv(1024)
    response = (
        "HTTP/1.1 200 OK\r\n"
        "Content-Type: text/plain\r\n"
        "Connection: close\r\n\r\n"
        "hello from raw server"
    ).encode()
    client.sendall(response)
    client.close()

server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind(("0.0.0.0", 8080))
server.listen()

while True:
    client, _ = server.accept()
    handle(client)

Удивительно, но такие маленькие упражнения формировали куда более мощное инженерное мышление, чем это может дать чарующий блеск современных фреймворков.

Комментарии (0)