Глава 15. Метафора выращивания — практика
"Программа — это живой организм, который растёт, адаптируется и эволюционирует."
15.1. Органический рост vs детальное планирование
Традиционный подход (Waterfall)
1. Требования (100% детализация) ─┐
2. Проектирование (полная архитектура) │ Планирование
3. Разработка (по спецификации) │ занимает 50%
4. Тестирование │ времени
5. Деплой ─┘
Проблема: К моменту деплоя требования уже изменились.
Growing Software подход
1. Ядро идеи (минимальный MVP) ─┐
2. Выращивание функций итерациями │ Рост
3. Адаптация под feedback │ происходит
4. Эволюция архитектуры │ постоянно
5. Continuous growth ─┘
Преимущество: Адаптация к изменениям в процессе.
15.2. Программа как живой организм
Три уровня аналогии
Уровень 1: Растение
Семя — начальная идея / MVP:
# Семя: простейший TODO app
todos = []
def add_todo(task):
todos.append(task)
def list_todos():
for i, task in enumerate(todos, 1):
print(f"{i}. {task}")
Росток — базовая функциональность:
# AI: добавь возможность отмечать задачи выполненными
todos = []
def add_todo(task):
todos.append({"task": task, "done": False})
def mark_done(index):
if 0 <= index < len(todos):
todos[index]["done"] = True
def list_todos():
for i, item in enumerate(todos, 1):
status = "✓" if item["done"] else " "
print(f"[{status}] {i}. {item['task']}")
Ветви — новые функции:
# AI: добавь приоритеты и сроки
def add_todo(task, priority="medium", due_date=None):
todos.append({
"task": task,
"done": False,
"priority": priority,
"due_date": due_date,
"created_at": datetime.now()
})
def get_by_priority(priority):
return [t for t in todos if t["priority"] == priority]
Плоды — продукт, который используют.
Уровень 2: Домашнее животное
Программа как питомец:
- Требует регулярного внимания (maintenance)
- Учится на ваших примерах (обучение на данных)
- Адаптируется к вашим привычкам
- Может "болеть" (баги) — нужно лечить
- Эволюционирует со временем
Пример: AI-агент Ева
# Ева запоминает мой стиль ответов
class Eva:
def __init__(self):
self.memory = [] # память о прошлых взаимодействиях
self.preferences = {} # мои предпочтения
def learn_from_feedback(self, response, feedback):
"""Ева учится на моих корректировках"""
self.memory.append({
"response": response,
"feedback": feedback,
"timestamp": datetime.now()
})
def adapt_style(self):
"""Анализирует память и адаптирует стиль"""
# AI анализирует паттерны в feedback
# Корректирует будущие ответы
pass
Как это работает:
- Ева генерирует ответ
- Я даю feedback ("слишком формально" / "хорошо")
- Ева запоминает
- Следующий ответ учитывает мои предпочтения
Уровень 3: Ребёнок
Программа как ребёнок:
- Начинает с простого (ползает)
- Постепенно усложняется (ходит, бегает)
- Учится на ошибках
- Требует наставника (вас!)
- Со временем становится самостоятельной
Пример эволюции программы:
Младенец (неделя 1):
# Простейший калькулятор
def add(a, b):
return a + b
Малыш (месяц 1):
# AI: расширь функциональность
class Calculator:
def add(self, a, b): return a + b
def subtract(self, a, b): return a - b
def multiply(self, a, b): return a * b
def divide(self, a, b):
if b == 0:
raise ValueError("Division by zero")
return a / b
Подросток (месяц 3):
# AI: добавь научные функции и историю вычислений
class ScientificCalculator(Calculator):
def __init__(self):
self.history = []
def calculate(self, operation, *args):
result = operation(*args)
self.history.append({
"operation": operation.__name__,
"args": args,
"result": result
})
return result
def sqrt(self, x): return math.sqrt(x)
def power(self, base, exp): return base ** exp
def log(self, x, base=math.e): return math.log(x, base)
Взрослый (месяц 6):
# AI: добавь UI, сохранение в БД, API
class CalculatorApp:
def __init__(self):
self.calculator = ScientificCalculator()
self.db = Database()
self.api = FastAPI()
@self.api.post("/calculate")
async def calculate_api(self, expression: str):
result = self.calculator.evaluate(expression)
await self.db.save_calculation(result)
return {"result": result}
15.3. От детерминизма к вероятности
Детерминистический код
def get_greeting(name):
return f"Hello, {name}!"
print(get_greeting("Alice")) # Всегда: "Hello, Alice!"
Характеристики:
- Результат всегда одинаковый
- Полный контроль
- Предсказуемо
- Тестируется точными значениями
Вероятностный код (с AI)
def generate_greeting(name, style="friendly"):
prompt = f"Generate a {style} greeting for {name}"
return llm.generate(prompt)
print(generate_greeting("Alice"))
# Вариант 1: "Hey Alice! How's it going?"
# Вариант 2: "Hi Alice! Great to see you!"
# Вариант 3: "Hello Alice! Hope you're having a wonderful day!"
Характеристики:
- Результат варьируется
- Направленный контроль (через промпт)
- Непредсказуемо в деталях, но предсказуемо в общем
- Тестируется паттернами, а не точными значениями
Как тестировать вероятностный код
Плохой подход:
def test_greeting():
result = generate_greeting("Alice")
assert result == "Hello, Alice!" # FAIL! Результат разный каждый раз
Хороший подход:
def test_greeting():
result = generate_greeting("Alice")
# Проверяем паттерн, а не точное значение
assert "Alice" in result # Имя должно быть
assert len(result) > 0 # Не пустой ответ
assert result.endswith(("!", ".", "?")) # Корректная пунктуация
# Проверяем тон (если важен)
sentiment = analyze_sentiment(result)
assert sentiment == "positive"
15.4. Практика: Выращивание TODO-приложения
Итерация 0: Семя
Промпт:
AI, создай простейшее TODO-приложение на Python.
Функции: добавить задачу, показать список.
Хранение в памяти (список).
Результат:
todos = []
def add_todo(task):
todos.append(task)
print(f"Added: {task}")
def list_todos():
if not todos:
print("No tasks yet!")
return
print("Your tasks:")
for i, task in enumerate(todos, 1):
print(f"{i}. {task}")
# CLI
while True:
cmd = input("Command (add/list/quit): ").strip().lower()
if cmd == "add":
task = input("Task: ")
add_todo(task)
elif cmd == "list":
list_todos()
elif cmd == "quit":
break
Итерация 1: Росток — отметка выполненных
Промпт:
AI, модифицируй код:
- Задачи должны иметь статус (done/not done)
- Добавь команду 'done <номер>' для отметки
- Отображай статус в списке
Результат:
todos = []
def add_todo(task):
todos.append({"task": task, "done": False})
print(f"Added: {task}")
def mark_done(index):
if 1 <= index <= len(todos):
todos[index - 1]["done"] = True
print(f"Marked as done: {todos[index - 1]['task']}")
else:
print("Invalid task number")
def list_todos():
if not todos:
print("No tasks yet!")
return
print("Your tasks:")
for i, item in enumerate(todos, 1):
status = "✓" if item["done"] else " "
print(f"[{status}] {i}. {item['task']}")
# CLI расширен
while True:
cmd = input("Command (add/list/done/quit): ").strip().lower()
if cmd == "add":
task = input("Task: ")
add_todo(task)
elif cmd == "list":
list_todos()
elif cmd.startswith("done"):
try:
index = int(cmd.split()[1])
mark_done(index)
except (IndexError, ValueError):
print("Usage: done <number>")
elif cmd == "quit":
break
Итерация 2: Ветви — приоритеты и сроки
Промпт:
AI, добавь:
- Приоритет задачи (high/medium/low)
- Срок выполнения (due_date)
- Команду 'filter priority <level>' для фильтрации
- Команду 'overdue' для просроченных задач
- Сортировку по приоритету и сроку
Результат: (код стал больше, показываю ключевые части)
from datetime import datetime, timedelta
todos = []
def add_todo(task, priority="medium", due_date=None):
todos.append({
"task": task,
"done": False,
"priority": priority,
"due_date": due_date,
"created_at": datetime.now()
})
def list_todos(filter_priority=None):
filtered = todos
if filter_priority:
filtered = [t for t in todos if t["priority"] == filter_priority]
# Сортировка: high > medium > low, потом по due_date
priority_order = {"high": 0, "medium": 1, "low": 2}
filtered.sort(key=lambda t: (
priority_order[t["priority"]],
t["due_date"] if t["due_date"] else datetime.max
))
for i, item in enumerate(filtered, 1):
status = "✓" if item["done"] else " "
priority_emoji = {"high": "🔴", "medium": "🟡", "low": "🟢"}[item["priority"]]
due = f"(due: {item['due_date'].strftime('%Y-%m-%d')})" if item["due_date"] else ""
print(f"[{status}] {priority_emoji} {i}. {item['task']} {due}")
def get_overdue():
now = datetime.now()
overdue = [t for t in todos if t["due_date"] and t["due_date"] < now and not t["done"]]
return overdue
Итерация 3: Плоды — Web UI + БД
Промпт:
AI, преобразуй приложение:
- Используй Flask для web-интерфейса
- Сохраняй задачи в SQLite
- Создай REST API:
GET /tasks - список
POST /tasks - создать
PUT /tasks/<id> - обновить
DELETE /tasks/<id> - удалить
- Добавь простой HTML/CSS фронтенд
Результат: (показываю backend)
from flask import Flask, request, jsonify, render_template
from flask_sqlalchemy import SQLAlchemy
from datetime import datetime
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///todos.db'
db = SQLAlchemy(app)
class Todo(db.Model):
id = db.Column(db.Integer, primary_key=True)
task = db.Column(db.String(200), nullable=False)
done = db.Column(db.Boolean, default=False)
priority = db.Column(db.String(10), default="medium")
due_date = db.Column(db.DateTime, nullable=True)
created_at = db.Column(db.DateTime, default=datetime.now)
@app.route('/api/tasks', methods=['GET'])
def get_tasks():
priority = request.args.get('priority')
query = Todo.query
if priority:
query = query.filter_by(priority=priority)
tasks = query.order_by(Todo.priority, Todo.due_date).all()
return jsonify([{
"id": t.id,
"task": t.task,
"done": t.done,
"priority": t.priority,
"due_date": t.due_date.isoformat() if t.due_date else None
} for t in tasks])
@app.route('/api/tasks', methods=['POST'])
def create_task():
data = request.json
new_task = Todo(
task=data['task'],
priority=data.get('priority', 'medium'),
due_date=datetime.fromisoformat(data['due_date']) if data.get('due_date') else None
)
db.session.add(new_task)
db.session.commit()
return jsonify({"id": new_task.id}), 201
@app.route('/api/tasks/<int:task_id>', methods=['PUT'])
def update_task(task_id):
task = Todo.query.get_or_404(task_id)
data = request.json
task.task = data.get('task', task.task)
task.done = data.get('done', task.done)
task.priority = data.get('priority', task.priority)
db.session.commit()
return jsonify({"success": True})
@app.route('/')
def index():
return render_template('index.html')
if __name__ == '__main__':
with app.app_context():
db.create_all()
app.run(debug=True)
Эволюция:
- Семя: 20 строк Python
- Росток: 50 строк
- Ветви: 100+ строк
- Плоды: Полноценное веб-приложение с БД
Время разработки с AI: ~2-3 часа вместо 2-3 дней!
15.5. Направление роста, а не контроль деталей
Садовник vs Микроменеджер
Микроменеджер (традиционное программирование):
# Вы контролируете каждую деталь
result = ""
for char in input_string:
if char.isalpha():
result += char.upper()
elif char.isdigit():
result += char
# ... ещё 50 строк деталей
Садовник (Growing Software):
# Вы направляете, AI реализует детали
prompt = """
Обработай строку:
- Буквы → в верхний регистр
- Цифры → оставить
- Остальное → удалить
"""
result = ai_process(prompt, input_string)
Как направлять рост
1. Начните с ядра (core value)
Не пытайтесь сразу создать всё. Начните с того, что даёт максимальную ценность.
Пример: Интернет-магазин
- ❌ НЕ начинайте с: регистрация, профили, отзывы, рекомендации
- ✅ Начните с: каталог товаров, корзина, оформление заказа
2. Растите итерациями
Каждая итерация добавляет 1-2 новые функции.
Итерация 1: Каталог + Корзина (MVP)
Итерация 2: Оформление заказа
Итерация 3: Регистрация пользователей
Итерация 4: История заказов
Итерация 5: Отзывы и рейтинги
3. Обрезайте лишнее (рефакторинг)
Как садовник обрезает ветки, вы удаляете:
- Неиспользуемый код
- Дублирующуюся логику
- Ненужные зависимости
AI, проанализируй код и найди:
- Неиспользуемые функции
- Дублирующуюся логику
- Места для рефакторинга
Предложи улучшения.
4. Удобряйте (улучшайте)
- Добавляйте тесты
- Улучшайте производительность
- Оптимизируйте UX
AI, добавь тесты для модуля заказов.
Покрытие должно быть >80%.
Используй pytest.
15.6. Практические упражнения
Упражнение 1: Вырастите блог
Итерация 0 (семя):
AI, создай простейший блог:
- Список постов (title, content)
- Добавление поста
- Хранение в памяти
- CLI интерфейс
Итерация 1-3: Самостоятельно определите, какие функции добавить.
Подсказка:
- Авторы постов?
- Категории / теги?
- Комментарии?
- Web UI?
Упражнение 2: Эволюция калькулятора
Начните с простого калькулятора (+, -, *, /).
Вырастите его до:
- Научного калькулятора
- С историей вычислений
- С графиками функций
- С web-интерфейсом
Итерации определите сами.
Упражнение 3: Анализ роста
Возьмите любой open-source проект на GitHub.
Посмотрите его историю коммитов (git log).
Проанализируйте:
- С чего начинался (первые коммиты)?
- Как рос (какие функции добавлялись)?
- Были ли "обрезки" (удаление функций)?
Вопрос: Это был "рост" или "строительство"?
Ключевые выводы главы
✅ Органический рост: Программа растёт итерациями, а не строится по плану
✅ Три уровня аналогии: Растение (семя→плоды), Животное (учится), Ребёнок (эволюционирует)
✅ От детерминизма к вероятности: AI-код тестируется паттернами, а не точными значениями
✅ Направление, а не контроль: Вы садовник, направляете рост, не контролируете детали
✅ Итеративный процесс: Семя → Росток → Ветви → Плоды
✅ С AI быстрее: Эволюция за 2-3 часа вместо 2-3 дней
✅ Обрезайте лишнее: Рефакторинг — это удаление ненужных веток
Следующая глава: Работа с LLM — интеграция AI в ваши программы