ЧАСТЬ II: ФУНДАМЕНТ МАСТЕРСТВА

Навык #1: Понимание кода

Глава 4. Навык #1: Понимание как работает код

"Код, который вы не понимаете, — это код, которому вы не можете доверять."


4.1. Почему понимание критично в эпоху AI

Представьте ситуацию:

Вы попросили AI создать функцию для обработки платежей. AI сгенерировал 50 строк кода. Код работает. Тесты проходят. Вы деплоите на продакшн.

Через неделю — утечка данных пользователей.

Что пошло не так?

AI сгенерировал код с уязвимостью SQL injection. Вы не заметили, потому что не понимали, что делает каждая строка.

Парадокс AI-разработки:

Чем проще генерировать код, тем важнее его понимать.

AI может написать код быстрее вас. Но AI:

  • ❌ Не понимает контекст вашего бизнеса
  • ❌ Может сгенерировать уязвимый код
  • ❌ Может использовать неоптимальный подход
  • ❌ Может не учесть edge cases

Вы — последняя линия защиты.

Ваша роль изменилась:

Раньше: Писатель кода Сейчас: Архитектор + Ревьюер + Отладчик

Но для всех этих ролей нужно понимать код.


4.2. Базовые концепции программирования

Программа — это инструкции для компьютера

Код — это способ сказать компьютеру: "Сделай вот так, шаг за шагом."

Пример (на человеческом языке):

1. Возьми два числа
2. Сложи их
3. Покажи результат

Пример (на Python):

def add(a, b):
    result = a + b
    return result

print(add(2, 3))  # Вывод: 5

Что здесь происходит:

  1. Мы определяем функцию add, которая принимает два параметра: a и b
  2. Внутри функции мы складываем a и b, результат сохраняем в переменную result
  3. Возвращаем result
  4. Вызываем функцию с аргументами 2 и 3, печатаем результат

Важно понимать КАЖДОЕ слово:

  • def — объявление функции
  • add — имя функции
  • (a, b) — параметры (входные данные)
  • : — начало блока кода
  • result = a + b — операция сложения и присваивание
  • return — возвращаемое значение
  • print() — вывод на экран

С AI: AI может сгенерировать этот код. Ваша задача — понять каждую строку.


4.3. Переменные и типы данных

Переменные — это контейнеры для данных

Аналогия: Коробка с наклейкой (имя переменной) и содержимым (значение).

name = "Шахруз"
age = 40
is_developer = True
  • name — строка (string)
  • age — целое число (integer)
  • is_developer — булево значение (boolean: True/False)

Типы данных

Примитивные типы:

Числа:

integer = 42          # Целое число
floating = 3.14       # Число с плавающей точкой

Строки:

text = "Growing Software"
char = 'A'

Булевы:

is_active = True
is_deleted = False

Составные типы:

Списки (массивы):

numbers = [1, 2, 3, 4, 5]
names = ["Ева", "Адам", "Софья"]

Словари (объекты):

user = {
    "name": "Шахруз",
    "age": 40,
    "role": "сеньор"
}

Почему типы важны?

Разные типы — разные операции:

a = 5
b = 3
print(a + b)  # 8 (сложение чисел)

x = "Hello"
y = "World"
print(x + y)  # HelloWorld (конкатенация строк)

# Ошибка!
z = "5" + 3   # TypeError: cannot concatenate str and int

С AI: AI знает типы, но может сгенерировать код, где типы конфликтуют. Вы должны заметить.


4.4. Память: стек и куча

Как компьютер хранит данные

Когда программа работает, она использует два вида памяти:

Стек (Stack):

  • Быстрая память
  • Хранит локальные переменные и вызовы функций
  • Автоматически освобождается
def calculate(x):
    result = x * 2  # result хранится в стеке
    return result

answer = calculate(5)  # answer хранится в стеке

Куча (Heap):

  • Медленная память
  • Хранит объекты и большие данные
  • Нужно освобождать вручную (в низкоуровневых языках) или через сборщик мусора
# Большой список хранится в куче
big_list = [i for i in range(1000000)]

Почему это важно?

Утечки памяти:

Если программа создаёт объекты в куче и не освобождает их, память заканчивается → программа падает.

# Плохо: утечка памяти
cache = []
while True:
    cache.append(large_data)  # Память растёт бесконечно

С AI: AI может сгенерировать код с утечкой памяти. Вы должны понимать, как работает память.


4.5. Циклы, условия, функции

Условия (if/else)

Выбор: сделать A или B в зависимости от условия.

age = 17

if age >= 18:
    print("Совершеннолетний")
else:
    print("Несовершеннолетний")

Вложенные условия:

score = 85

if score >= 90:
    grade = "A"
elif score >= 80:
    grade = "B"
elif score >= 70:
    grade = "C"
else:
    grade = "F"

Циклы (loops)

Повторение действия несколько раз.

For loop:

# Вывести числа от 1 до 5
for i in range(1, 6):
    print(i)

While loop:

# Вывести числа, пока они меньше 5
i = 1
while i <= 5:
    print(i)
    i += 1

Опасность бесконечного цикла:

# Плохо: бесконечный цикл
while True:
    print("Навсегда!")  # Программа зависнет

С AI: AI может сгенерировать бесконечный цикл по ошибке. Вы должны заметить.

Функции

Переиспользуемый блок кода.

def greet(name):
    return f"Привет, {name}!"

print(greet("Шахруз"))  # Привет, Шахруз!
print(greet("Ева"))     # Привет, Ева!

Параметры и аргументы:

def multiply(a, b):  # a, b — параметры
    return a * b

result = multiply(3, 4)  # 3, 4 — аргументы

Возвращаемое значение:

def add(x, y):
    return x + y  # Возвращает результат

# Без return
def print_sum(x, y):
    print(x + y)  # Только печатает, не возвращает

result1 = add(2, 3)        # result1 = 5
result2 = print_sum(2, 3)  # result2 = None (печатает 5)

4.6. Асинхронность и параллелизм

Синхронное выполнение (обычное)

Код выполняется последовательно, строка за строкой.

print("1")
print("2")
print("3")

# Вывод:
# 1
# 2
# 3

Асинхронное выполнение

Код может выполняться не по порядку.

Зачем? Чтобы не ждать медленных операций (запрос к API, чтение файла).

import asyncio

async def fetch_data():
    print("Начало запроса...")
    await asyncio.sleep(2)  # Симуляция медленного запроса
    print("Данные получены!")
    return "data"

async def main():
    print("Запускаем...")
    data = await fetch_data()
    print(f"Результат: {data}")

asyncio.run(main())

# Вывод:
# Запускаем...
# Начало запроса...
# (ждём 2 секунды)
# Данные получены!
# Результат: data

Параллелизм

Несколько задач выполняются одновременно.

import asyncio

async def task1():
    await asyncio.sleep(1)
    print("Task 1 done")

async def task2():
    await asyncio.sleep(1)
    print("Task 2 done")

async def main():
    await asyncio.gather(task1(), task2())

asyncio.run(main())

# Обе задачи выполнятся параллельно за 1 секунду (а не 2)

С AI: Асинхронный код сложнее. AI может ошибиться с await. Вы должны понимать, как работает асинхронность.


4.7. Структуры данных (массивы, объекты, списки)

Массивы/Списки

Упорядоченная коллекция элементов.

fruits = ["apple", "banana", "orange"]

# Доступ по индексу (начинается с 0)
print(fruits[0])  # apple
print(fruits[1])  # banana

# Добавление элемента
fruits.append("grape")

# Длина списка
print(len(fruits))  # 4

# Итерация
for fruit in fruits:
    print(fruit)

Словари (хеш-таблицы, объекты)

Коллекция пар ключ-значение.

user = {
    "name": "Шахруз",
    "age": 40,
    "city": "Ташкент"
}

# Доступ по ключу
print(user["name"])  # Шахруз

# Добавление/изменение
user["role"] = "сеньор"
user["age"] = 41

# Итерация
for key, value in user.items():
    print(f"{key}: {value}")

Множества (Sets)

Неупорядоченная коллекция уникальных элементов.

numbers = {1, 2, 3, 3, 4}
print(numbers)  # {1, 2, 3, 4} — дубликаты удалены

Когда использовать что:

  • Список — когда важен порядок
  • Словарь — когда нужен быстрый доступ по ключу
  • Множество — когда нужны уникальные элементы

4.8. Практика: читаем и понимаем AI-код

Упражнение 1: Анализ AI-кода

AI сгенерировал этот код. Что он делает? Есть ли проблемы?

def process_data(data):
    result = []
    for item in data:
        if item > 0:
            result.append(item * 2)
    return result

numbers = [1, -2, 3, 0, 5]
output = process_data(numbers)
print(output)

Ваш анализ:

  1. Что делает функция?
  2. Какой будет output?
  3. Есть ли edge cases, которые не учтены?
  4. Можно ли улучшить?
Ответ
  1. Что делает: Фильтрует положительные числа и умножает на 2
  2. Output: [2, 6, 10]
  3. Edge cases: Что если data пустой? Что если элементы не числа?
  4. Улучшение:
def process_data(data):
    if not data:
        return []

    result = []
    for item in data:
        if isinstance(item, (int, float)) and item > 0:
            result.append(item * 2)
    return result

Упражнение 2: Найди ошибку

AI сгенерировал код для подсчёта среднего:

def calculate_average(numbers):
    total = 0
    for num in numbers:
        total += num
    return total / len(numbers)

scores = [80, 90, 70]
avg = calculate_average(scores)
print(f"Средний балл: {avg}")

Проблема: Что будет, если передать пустой список?

calculate_average([])  # ZeroDivisionError!

Исправление:

def calculate_average(numbers):
    if not numbers:
        return 0

    total = sum(numbers)
    return total / len(numbers)

Упражнение 3: Оптимизация

AI сгенерировал код для проверки, есть ли дубликаты:

def has_duplicates(items):
    for i in range(len(items)):
        for j in range(i + 1, len(items)):
            if items[i] == items[j]:
                return True
    return False

# Тестируем
print(has_duplicates([1, 2, 3, 4]))  # False
print(has_duplicates([1, 2, 3, 1]))  # True

Вопрос: Какая сложность алгоритма? (O(?))

Ответ

Сложность: O(n²) — два вложенных цикла

Проблема: Медленно для больших списков

Оптимизация: Использовать множество (set) — O(n)

def has_duplicates(items):
    return len(items) != len(set(items))

# Или более явно:
def has_duplicates(items):
    seen = set()
    for item in items:
        if item in seen:
            return True
        seen.add(item)
    return False

4.9. Упражнения

Задание 1: Понимание кода

Попросите AI сгенерировать функцию для проверки, является ли строка палиндромом.

Затем:

  1. Прочитайте код построчно
  2. Объясните, что делает каждая строка
  3. Найдите edge cases
  4. Улучшите код

Задание 2: Поиск ошибок

Попросите AI создать функцию для расчёта факториала.

Затем:

  1. Проверьте на edge cases: factorial(0), factorial(-5)
  2. Есть ли риск переполнения для больших чисел?
  3. Есть ли риск бесконечной рекурсии?

Задание 3: Оптимизация

Попросите AI создать функцию для поиска наибольшего элемента в списке.

Затем:

  1. Проверьте сложность алгоритма
  2. Можно ли сделать быстрее?
  3. Что будет, если список пустой?

Ключевые выводы главы

Понимание важнее генерации: AI пишет код, вы понимаете

Каждая строка имеет значение: Читайте код построчно

Типы данных критичны: Разные типы → разные операции

Память имеет значение: Стек и куча, утечки памяти

Асинхронность сложна: AI может ошибиться

Edge cases важны: Пустые списки, null, отрицательные числа

Всегда проверяйте AI-код: Ошибки, уязвимости, оптимизация


Следующая глава: Архитектурное мышление — проектирование систем с AI