Upgrade to Pro — share decks privately, control downloads, hide ads and more …

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

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

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

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

Moscow Python Meetup
PRO

January 30, 2020
Tweet

More Decks by Moscow Python Meetup

Other Decks in Programming

Transcript

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

    View Slide

  2. View Slide

  3. View Slide

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

    View Slide

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

    View Slide

  6. View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  14. SaaS!

    View Slide

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

    View Slide

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

    View Slide

  17. Swarm!

    View Slide

  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"

    View Slide

  19. Стройте простую инфраструктуру

    View Slide

  20. View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  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

    View Slide

  25. pytest
    ● Бизнес-логика только
    на бекенде

    View Slide

  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);
    });
    });

    View Slide

  27. pytest
    ● Бизнес-логика только
    на бекенде
    ● Если очень надо логику
    на фронте — TDD

    View Slide

  28. Пара рекомендаций

    View Slide

  29. View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  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

    View Slide

  35. Пишите короткие юнит-тесты

    View Slide

  36. View Slide

  37. Django templates

    {% for choice in poll.choice_set.all %}

    {{ choice.choice }}
    {% endfor %}


    View Slide

  38. Any frontend framework!

    View Slide

  39. Django admin

    View Slide

  40. Any frontend framework!

    View Slide

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

    View Slide

  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!

    View Slide

  43. ORM + ServiceObjects + API

    View Slide

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

    View Slide