Django в стартапе: от 0 до 150 000 строк кода, не жертвуя качеством

Django в стартапе: от 0 до 150 000 строк кода, не жертвуя качеством

Фёдор Борщёв (ГдеМатериал) @ Moscow Python № 72
"Речь пойдет о том, как мы поддерживаем здоровье кодовой базы в проекте с безумными требованиями к скорости и постоянно меняющимися задачами. Мы поговорим про TDD, SOLID и KISS там, где люди меньше всего к этому готовы — в стартапе, который доставляет стройматериалы".

Видео: http://www.moscowpython.ru/meetup/72/django-in-startup/

53b0434aded1fb944ec3037c382158c1?s=128

Moscow Python Meetup

January 30, 2020
Tweet

Transcript

  1. Django в стартапе Как не пожертвовать качеством Федя Борщёв, t.me/pmdaily

  2. None
  3. None
  4. Большие компании Стартапы

  5. Time to market — основная метрика разработки

  6. None
  7. О ГдеМатериале

  8. 6 разработчиков

  9. Что влияет на счастье?

  10. Что влияет на счастье?

  11. Что влияет на счастье?

  12. Стандарт CI для больших компаний

  13. Нужно поддерживать флот

  14. SaaS!

  15. Стандарт для больших компаний

  16. Высокий порог входа

  17. Swarm!

  18. Traefik: Понятный синтаксис, автосертификаты deploy: replicas: 2 labels: - "traefik.enable=true"

    - "traefik.port=8000" - "traefik.frontend.rule=Host:app.gdml.ru; PathPrefix:/api/,/admin/" - "traefik.docker.network=traefiknet"
  19. Стройте простую инфраструктуру

  20. None
  21. Тестирование для больших компаний

  22. Отстают от кода, нужно поддерживать

  23. Тесты, которые точно не отстанут от кода

  24. Тестировать ручки API def test_create_with_author(api, author): api.post('/api/v1/leads/', { 'customer': {'phone':

    '+7 999 999-99-99'}, 'author': author.id, }) got = Lead.objects.last() assert got.author == author
  25. pytest • Бизнес-логика только на бекенде

  26. Если надо бизнес-логику на фронт — TDD describe('ADD_ITEM mutation', ()

    => { it('Increases item quantity when adding the same item', () => { const state = store.state(); const product = productFactory({ id: 100500 }); store.mutations.ADD_ITEM(state, { product }); store.mutations.ADD_ITEM(state, { product }); expect(state.items).toHaveLength(1); expect(state.items[0].quantity).toEqual(2); }); });
  27. pytest • Бизнес-логика только на бекенде • Если очень надо

    логику на фронте — TDD
  28. Пара рекомендаций

  29. None
  30. Тест — это история

  31. Тест — это история

  32. Тест — это история

  33. Сетап → Действие → Асёрт

  34. Две истории def test_class_marked_as_used(self): c = self._buy_a_lesson() entry = self._create_entry()

    self._schedule(c, entry) c.refresh_from_db() self.assertTrue(c.is_scheduled) with freeze_time('2032-12-01 16:30'): bill_timeline_entries() # run the periodic task by hand with self.assertRaises(ObjectDoesNotExist): entry.refresh_from_db() c.refresh_from_db() self.assertTrue(c.is_fully_used) self.assertEqual(AccEvent.objects.count(), 1) @pytest.fixture der _klass(factory, schedule): def create_a_class(schedule_to: datetime): klass = factory.klass() schedule(klass, date=schedule_to) return klass return _klass @pytest.mark.freeze_time('2032-12-01 16:30') def test_marking_class_as_used(_klass, freezer): klass = _klass(schedule_to='2032-11-01 15:30') # in the past bill_timeline_entries() # assert klass.is_fully_used is True
  35. Пишите короткие юнит-тесты

  36. None
  37. Django templates <form action="/polls/{{ poll.id }}/vote/" method="POST"> {% for choice

    in poll.choice_set.all %} <input type = "radio" name="choice" id="choice_{{ forloop.counter }} value="{{ choice.id}}" /> <label for="choice_{{ forloop.counter }}">{{ choice.choice }}</label> {% endfor %} <input type="submit" /> </form>
  38. Any frontend framework!

  39. Django admin

  40. Any frontend framework!

  41. Толстые модели

  42. class UserCreator: """Service object for creating a user""" def __init__(self,

    name, email, subscribe=True): self.do_subscribe = subscribe self.data = { 'email': email, 'username': email or str(uuid.uuid4()), 'subscribed': subscribe, **User.parse_name(name), } def __call__(self) -> User: self.resulting_user = self.get() or self.create() self.after_creation() return self.resulting_user Service Objects!
  43. ORM + ServiceObjects + API

  44. Федя Борщёв borshev.com t.me/pmdaily