Как компании начать разрабатывать собственное ПО: полное практическое руководство
Автор: Инженер-программист
От бизнес-задачи до работающей системы за 6 месяцев
Почему собственное ПО — это не про экономию, а про контроль
Реальная история:
Производственная компания (оборот 5 млрд руб.) использовала комбинацию: 1С для учёта, западная MES для производства, самописные Excel-макросы для планирования. Проблемы появились в 2022:
- MES перестал обновляться (санкции)
- Интеграции ломались каждый месяц
- Данные не сходились между системами
- Изменения требовали 3 месяца согласований
Решение: Разработали собственную систему за 8 месяцев (бюджет 25 млн руб.). Результат через год:
- Time-to-market новых продуктов: с 6 месяцев до 3 недель
- Интеграционные ошибки: -85%
- Независимость от вендоров: 100%
- Окупаемость: 14 месяцев
Вывод: Собственное ПО — это стратегический актив, а не IT-проект.
Шаг 1: Определить границы — что писать, а что покупать
Матрица принятия решений
| Критерий | Писать самим | Покупать готовое |
|---|---|---|
| Критичность для бизнеса | Ядро процессов | Вспомогательные функции |
| Уникальность | Конкурентное преимущество | Стандартные операции |
| Частота изменений | > 1 раза в месяц | < 1 раза в год |
| Интеграции | Сложная связка систем | Изолированная функция |
| Регуляторика | Специфические требования | Общие стандарты |
| Риски блокировки | Зависимость от вендора | Стабильный рынок |
Примеры задач для собственной разработки
Писать самим:
- Диспетчеризация производства
- Уникальная технология
- Интеграция с 15+ системами
- Изменения каждую неделю
- Пример: управление линиями розлива с учётом специфики рецептур
- Система ценообразования
- Сложные алгоритмы расчёта
- Десятки переменных (курсы, склады, логистика)
- Конкурентное преимущество
- Пример: динамическое ценообразование для ритейла
- Интеграционная шина
- Связь ERP ↔ MES ↔ SCADA ↔ BI
- Трансформация данных
- Управление потоками
- Пример: единая точка входа для всех систем
Не писать (купить готовое):
- CRM (Битрикс24, amoCRM)
- Бухгалтерия (1С, SAP)
- Email-рассылки (SendPulse, Unisender)
- Видеоконференции (Яндекс.Телемост, Сферум)
Чек-лист принятия решения
Писать самим, если 3 и более пункта совпадают:
- Задача критична для основного бизнеса
- Готовых решений нет или они не подходят на 80%+
- Требуется глубокая интеграция с другими системами
- Нужны частые изменения (еженедельно/ежемесячно)
- Есть риски блокировки вендора
- Планируем масштабирование на 3+ года
Шаг 2: Архитектура — думать слоями, а не монолитом
Базовая 5-слойная модель
┌─────────────────────────────────────┐
│ Слой 5: Представление (UI/UX) │
│ • Веб-интерфейсы │
│ • Мобильные приложения │
│ • Дашборды │
├─────────────────────────────────────┤
│ Слой 4: API / Интеграции │
│ • REST/GraphQL │
│ • Message queues (RabbitMQ/Kafka) │
│ • Event bus │
├─────────────────────────────────────┤
│ Слой 3: Бизнес-логика │
│ • Расчёты │
│ • Правила │
│ • Workflow │
├─────────────────────────────────────┤
│ Слой 2: Данные │
│ • PostgreSQL (транзакции) │
│ • ClickHouse (аналитика) │
│ • Redis (кэш) │
├─────────────────────────────────────┤
│ Слой 1: Безопасность / Управление │
│ • Аутентификация (Keycloak) │
│ • Аудит │
│ • Мониторинг │
└─────────────────────────────────────┘
Пример: система управления складом
Неправильно (монолит):
[Одно приложение]
├── Учёт товаров
├── Приёмка
├── Отгрузка
├── Инвентаризация
├── Интеграция с 1С
└── Отчёты
Проблемы:
- Изменение отчётов требует полного релиза
- Падение приёмки = падение всей системы
- Невозможно масштабировать отдельные функции
Правильно (модульный монолит):
[Ядро WMS]
├── Модуль: Inventory (учёт остатков)
├── Модуль: Receiving (приёмка)
├── Модуль: Shipping (отгрузка)
└── Модуль: Reporting (отчёты)
[Интеграционный слой]
├── Connector: 1С (REST API)
├── Connector: TMS (транспорт)
└── Event Bus (RabbitMQ)
[UI слой]
├── Web-кабинет кладовщика
└── Мобильное приложение (ТСД)
Преимущества:
- Модули развиваются независимо
- Отказ одного модуля не роняет систему
- Можно масштабировать по нагрузке
- Легко передать разным командам
Шаг 3: Технологический стек — прагматично, а не модно
Матрица выбора по задачам
| Задача | Рекомендация | Альтернатива | Когда НЕ подходит |
|---|---|---|---|
| Backend (бизнес-логика) | Java/Spring Boot | Python/FastAPI, C#/.NET | Нужен прототип за неделю |
| API-сервисы | Go, Node.js | Java, Python | Сложная бизнес-логика |
| Аналитика/ML | Python | R, Julia | Real-time обработка |
| Frontend корп. | TypeScript + React | Vue, Angular | Простые формы (используйте low-code) |
| Мобильные приложения | Flutter | React Native, нативно | Нужны специфичные API устройств |
| Real-time/IoT | Go, Rust | C++, Node.js | Нет требований к скорости |
| Интеграции | Python, Node.js | Java | — |
Рекомендация для старта (команда 3–5 человек)
Минимальный стек:
- Backend: Java 17 + Spring Boot (стабильность, enterprise-ready)
- Frontend: TypeScript + React (большое комьюнити)
- База данных: PostgreSQL (универсальность)
- Очереди: RabbitMQ (простота)
- Кэш: Redis
- Мониторинг: Prometheus + Grafana
Почему именно так:
- Один язык для backend (Java) = легче поддерживать
- TypeScript = меньше ошибок в продакшене
- PostgreSQL = решает 90% задач
- Все инструменты open source = нет vendor lock-in
Антипаттерн: зоопарк технологий
Плохо (реальный кейс):
- Backend: Java + Python + Go + Node.js
- Frontend: React + Vue + Angular
- БД: PostgreSQL + MySQL + MongoDB + Redis
- Очереди: RabbitMQ + Kafka + Redis Pub/Sub
Результат: Команда из 8 человек тратит 60% времени на синхронизацию, а не на фичи.
Правило: 1 язык для backend, 1 для frontend, 1 БД для транзакций, 1 для аналитики.
Шаг 4: Безопасность — закладывается сразу, а не «потом»
Принципы Security by Design
1. Минимальные привилегии
Плохо:
sql
-- Приложение работает от суперпользователя
GRANT ALL PRIVILEGES ON DATABASE myapp TO app_user;
Правильно:
sql
-- Отдельные роли для чтения и записи
CREATE ROLE app_reader;
GRANT SELECT ON ALL TABLES IN SCHEMA public TO app_reader;
CREATE ROLE app_writer;
GRANT INSERT, UPDATE ON specific_tables TO app_writer;
```
**2. Разделение контуров**
```
[Production] [Staging] [Development]
↓ ↓ ↓
Реальные данные Анонимизация Тестовые данные
Доступ: 2 чел. Доступ: 5 чел. Доступ: все
VPN обязателен VPN обязателен Локально
3. Шифрование
Обязательно шифровать:
- Пароли (bcrypt/Argon2, НИКОГДА MD5)
- Персональные данные (ФИО, паспорта, СНИЛС)
- Финансовые данные (счета, карты)
- API ключи и токены
- Backup-копии
Пример (Java):
java
// Шифрование данных AES-256
public class DataEncryptor {
private static final String ALGORITHM = "AES/GCM/NoPadding";
public byte[] encrypt(String data, SecretKey key) {
Cipher cipher = Cipher.getInstance(ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE, key);
return cipher.doFinal(data.getBytes(UTF_8));
}
}
4. Аудит всех действий
Что логировать:
- Вход/выход пользователей
- Изменения критичных данных (суммы, статусы)
- Изменения прав доступа
- Экспорт данных
- API-запросы с результатами
Формат лога:
json
{
"timestamp": "2025-01-09T14:30:00Z",
"user_id": "ivan.petrov@company.com",
"action": "update_order_status",
"resource": "order_12345",
"old_value": "pending",
"new_value": "approved",
"ip": "192.168.1.100",
"session_id": "abc123"
}
Чек-лист безопасности перед запуском
- Все пароли хэшированы (bcrypt/Argon2)
- HTTPS для всех соединений
- SQL injection защита (prepared statements)
- XSS защита (экранирование HTML)
- CSRF токены для форм
- Rate limiting для API (макс. 100 req/min)
- Secrets в Vault, не в коде
- Логирование всех изменений
- Backup ежедневно, хранение 30 дней
- Тестирование на проникновение (пентест)
Шаг 5: Интеграции — как связать с другими системами
Типы интеграций и их реализация
1. Синхронная интеграция (REST API)
Когда использовать:
- Нужен немедленный ответ
- Небольшой объём данных (< 1000 записей)
- Критична актуальность (цены, остатки)
Пример: получение данных из 1С
python
import requests
def get_product_price(product_id):
response = requests.get(
f"https://1c.company.com/api/products/{product_id}",
auth=('api_user', 'password'),
timeout=5
)
if response.status_code == 200:
return response.json()['price']
else:
# Fallback: использовать кэш
return get_cached_price(product_id)
2. Асинхронная интеграция (Message Queue)
Когда использовать:
- Большой объём данных
- Не критична скорость
- Нужна гарантия доставки
Пример: отправка заказа в ERP
python
import pika
def send_order_to_erp(order_data):
connection = pika.BlockingConnection(
pika.ConnectionParameters('rabbitmq.company.local')
)
channel = connection.channel()
channel.queue_declare(queue='erp_orders', durable=True)
channel.basic_publish(
exchange='',
routing_key='erp_orders',
body=json.dumps(order_data),
properties=pika.BasicProperties(
delivery_mode=2 # сообщение персистентно
)
)
connection.close()
```
**3. Событийная интеграция (Event Bus)**
**Когда использовать:**
- Множество систем должны знать о событии
- Decoupling (системы не знают друг о друге)
- Асинхронная обработка
**Схема:**
```
[WMS: Товар отгружен] → [Event Bus] → Подписчики:
├─ [1С: Списать со склада]
├─ [CRM: Уведомить клиента]
├─ [BI: Обновить аналитику]
└─ [Логистика: Создать ТТН]
Паттерн: Anticorruption Layer (ACL)
Проблема: 1С возвращает данные в неудобном формате.
Решение: Адаптер, который трансформирует данные.
java
// Адаптер для 1С
public class OneCAdapter {
private OneCApiClient client;
public Product getProduct(String id) {
// Получаем данные из 1С
OneCProduct raw = client.fetchProduct(id);
// Преобразуем в нашу модель
return Product.builder()
.id(raw.getCode())
.name(raw.getDescription())
.price(raw.getPrice().divide(100)) // копейки → рубли
.available(raw.getStock() > 0)
.build();
}
}
```
**Преимущества:**
- Наша система не зависит от структуры данных 1С
- Легко заменить 1С на другую ERP
- Все изменения в одном месте
---
## Шаг 6: Работа с реальными данными (IoT, датчики, оборудование)
### Архитектура сбора данных
```
[Датчики/PLC] → [Edge-шлюз] → [MQTT Broker] → [Backend] → [БД]
↓
Предобработка
Фильтрация
Буферизация
Пример: мониторинг температуры в холодильных камерах
1. Edge-устройство (Raspberry Pi с датчиками)
python
import paho.mqtt.client as mqtt
import time
from w1thermsensor import W1ThermSensor
# Подключение к MQTT
client = mqtt.Client()
client.connect("mqtt.company.local", 1883)
sensor = W1ThermSensor()
while True:
temp = sensor.get_temperature()
# Отправка в MQTT
client.publish(
"factory/warehouse_1/temperature",
json.dumps({
"value": temp,
"timestamp": time.time(),
"sensor_id": "temp_001"
})
)
time.sleep(60) # каждую минуту
2. Backend (обработка данных)
java
@Service
public class TemperatureMonitor {
@Autowired
private AlertService alertService;
@MqttSubscribe("factory/+/temperature")
public void handleTemperature(TemperatureReading reading) {
// Сохраняем в БД
temperatureRepo.save(reading);
// Проверяем пороги
if (reading.getValue() > 5.0) {
alertService.sendAlert(
"Превышение температуры в камере: " +
reading.getValue() + "°C"
);
}
}
}
3. Хранение временных рядов (TimescaleDB)
sql
-- Таблица для временных рядов
CREATE TABLE temperature_readings (
time TIMESTAMPTZ NOT NULL,
sensor_id TEXT NOT NULL,
value DOUBLE PRECISION,
warehouse TEXT
);
SELECT create_hypertable('temperature_readings', 'time');
-- Запрос: средняя температура за последний час
SELECT
time_bucket('5 minutes', time) AS bucket,
AVG(value) as avg_temp
FROM temperature_readings
WHERE time > NOW() - INTERVAL '1 hour'
GROUP BY bucket
ORDER BY bucket;
Шаг 7: Масштабирование — техническое и организационное
Техническое масштабирование
Горизонтальное (добавление серверов):
yaml
# docker-compose.yml (упрощённо)
version: '3.8'
services:
app:
image: myapp:latest
deploy:
replicas: 3 # 3 экземпляра приложения
environment:
- DATABASE_URL=postgresql://db:5432/myapp
nginx:
image: nginx:latest
ports:
- "80:80"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf
db:
image: postgres:15
volumes:
- pgdata:/var/lib/postgresql/data
Кэширование:
java
@Service
public class ProductService {
@Cacheable(value = "products", key = "#id")
public Product getProduct(String id) {
// Тяжёлый запрос выполнится только раз
return productRepo.findById(id);
}
@CacheEvict(value = "products", key = "#product.id")
public void updateProduct(Product product) {
productRepo.save(product);
}
}
Организационное масштабирование
Документация как код:
markdown
# docs/architecture/adr/001-use-postgresql.md
# ADR-001: Использование PostgreSQL как основной БД
## Статус
Принято (2025-01-09)
## Контекст
Нужна надёжная реляционная БД для хранения заказов,
продуктов и пользователей.
## Решение
PostgreSQL 15
## Альтернативы
- MySQL — отвергнуто (лицензирование Oracle)
- MongoDB — отвергнуто (нужны ACID-транзакции)
## Последствия
+ Поддержка JSON
+ Полнотекстовый поиск
+ Зрелая экосистема
- Требует настройки производительности
Code review как стандарт:
markdown
# .github/PULL_REQUEST_TEMPLATE.md
## Что изменено
<!-- Описание изменений -->
## Зачем
<!-- Ссылка на задачу: Closes #123 -->
## Как проверить
1. Запустить `docker-compose up`
2. Открыть http://localhost:8080
3. Выполнить действия X, Y, Z
## Чек-лист
- [ ] Тесты добавлены/обновлены
- [ ] Документация обновлена
- [ ] Нет breaking changes
- [ ] Code coverage > 70%
Шаг 8: Roadmap от идеи до продакшена (6 месяцев)
Месяц 1: Фундамент
Недели 1–2: Проектирование
- Event storming (выявление bounded contexts)
- Выбор технологического стека
- Схема архитектуры (C4 model)
- Определение API-контрактов
Недели 3–4: Инфраструктура
- Поднять Git-сервер (GitLab on-prem)
- Настроить CI/CD
- Развернуть dev/staging окружения
- Настроить мониторинг
Месяц 2–3: MVP
Цель: Работающее ядро системы с 2–3 ключевыми функциями.
Недели 5–8:
- Реализовать базовые CRUD операции
- Интегрировать с 1 внешней системой
- Написать unit-тесты (покрытие > 70%)
- Создать базовый UI
Недели 9–12:
- Интеграционные тесты
- Нагрузочное тестирование (JMeter)
- Документация API (Swagger)
- Демо для стейкхолдеров
Месяц 4–5: Доработка и безопасность
Недели 13–16:
- Внедрить аутентификацию (OAuth2/Keycloak)
- Настроить аудит действий
- Шифрование критичных данных
- Пентест (внутренний или внешний)
Недели 17–20:
- Оптимизация производительности
- Добавить недостающие функции
- User Acceptance Testing (UAT)
- Исправление багов
Месяц 6: Запуск
Недели 21–22: Подготовка
- Миграция данных из старой системы
- Обучение пользователей
- Runbook для production
- План отката (rollback)
Недели 23–24: Go-live
- Запуск в production
- Мониторинг 24/7 первую неделю
- Горячая линия для пользователей
- Быстрые исправления по результатам
Недели 25–26: Стабилизация
- Анализ метрик использования
- Сбор обратной связи
- Приоритизация доработок
- Планирование версии 2.0
Бюджет и команда: реальные цифры
Минимальная команда (3 человека)
| Роль | Зона ответственности | Зарплата/мес |
|---|---|---|
| Tech Lead / Backend | Архитектура, API, интеграции | 250–350 тыс. |
| Frontend Developer | UI, UX, мобильные клиенты | 150–250 тыс. |
| DevOps / QA | CI/CD, тесты, инфраструктура | 150–250 тыс. |
Итого: 550–850 тыс. руб./мес × 6 месяцев = 3.3–5.1 млн руб. (только з/п).
Дополнительные расходы
| Статья | Стоимость |
|---|---|
| Серверы (on-prem) | 1–2 млн (единоразово) |
| Лицензии (GitLab EE, IDE) | 500 тыс./год |
| Обучение команды | 300–500 тыс. |
| Пентест | 200–400 тыс. |
| Непредвиденное (15%) | 700 тыс.–1 млн |
Итого за 6 месяцев: 6–9 млн руб.
Альтернатива: подрядчик
Разработка «под ключ»: 10–25 млн руб.
Но: Вы не владеете знаниями, зависимость от подрядчика, поддержка +30% ежегодно.
Вывод: Собственная команда окупается через 12–18 месяцев.
Главные принципы
- Начинайте с архитектуры, а не с кода: Без понятной структуры система развалится через год.
- Безопасность — с первого дня: «Добавим потом» = никогда не добавите правильно.
- Минимум технологий: 1 язык для backend, 1 для frontend, 1 БД = легче поддерживать.
- Документируйте решения: ADR сохранит знания при смене команды.
- CI/CD с первого коммита: Ручной деплой = дорого и опасно.
- Интеграции через адаптеры: ACL защитит от изменений внешних систем.
- Масштабируйтесь постепенно: Микросервисы нужны при 100+ разработчиках, не раньше.
Главная мысль:
Собственное ПО — это не «написать приложение», а построить управляемую систему, которая будет служить бизнесу 10+ лет. Инвестиция в правильную архитектуру окупается многократно через снижение TCO и независимость от вендоров.
