|
| 1 | +## Flagsmith API |
| 2 | + |
| 3 | +### Local development |
| 4 | + |
| 5 | +The project assumes the following tools installed: |
| 6 | +- [Python](https://www.python.org/downloads/). Any version allowed by `requires-python` in `pyproject.toml` is supported. |
| 7 | +- [GNU Make](https://www.gnu.org/software/make/). |
| 8 | +- Docker or a compatible tool like [Podman](https://podman.io/). We recommend [OrbStack](https://orbstack.dev/) for macOS. |
| 9 | + |
| 10 | +To install dev dependencies, run `make install`. |
| 11 | + |
| 12 | +To run linters, run `make lint`. |
| 13 | + |
| 14 | +To run tests, run `make docker-up test`. |
| 15 | + |
| 16 | +To prepare a dev database, run `make docker-up django-migrate`. |
| 17 | + |
| 18 | +To bring up a dev server, run `make serve`, or `make serve-with-task-processor` to run the Task processor alongside the server. |
| 19 | + |
| 20 | +### Code guidelines: testing |
| 21 | + |
| 22 | +The required diff test coverage for our backend PRs is 100%. This policy gives us more confidence to ship, helps us to find bugs earlier, and promotes the test-driven development (TDD) approach. We encourage you to add new tests, and modify existing ones, ahead of writing the code. |
| 23 | + |
| 24 | +This codebase includes two kinds of tests: |
| 25 | +- Black box API tests in `tests/integration` directory. Ideally, these are intended to only invoke API endpoints, and verify their output. |
| 26 | +- Tests for individual modules, classes and functions in `tests/unit` directory. |
| 27 | + |
| 28 | +We avoid class-based tests. To manage test lifecycle and dependencies, we rely on Pytest features such as fixtures, markers, parametrisation, and hooks. Read `conftest.py` for commonly used fixtures. |
| 29 | + |
| 30 | +We recommend naming test functions using the `test_{subject}__{condition}__{expected outcome}` template, e.g. `test_get_version__valid_file_contents__returns_version_number`. |
| 31 | + |
| 32 | +We use the Given When Then structure in all our tests. |
| 33 | + |
| 34 | +### Code guidelines: migrations |
| 35 | + |
| 36 | +To auto-generate migrations for your new code, run `make docker-up django-make-migrations`. |
| 37 | + |
| 38 | +The prompt will ask you for a name and not generate one; we avoid auto-generated migration names. |
| 39 | + |
| 40 | +Squash newly added migrations whenever you can. |
| 41 | + |
| 42 | +### Code guidelines: typing |
| 43 | + |
| 44 | +This codebase, including tests, is fully type-checked by Mypy in strict mode. Resolving existing `# type: ignore` comments is always welcome. If you happen to bring a new `# type: ignore` comment, please document the reason, and consider fixing a small number of adjacent `# type: ignore` comments, if possible and appropriate for the scope of your task. |
| 45 | + |
| 46 | +To run a full type check, run `make typecheck`. |
| 47 | + |
| 48 | +### Code guidelines: design and architecture |
| 49 | + |
| 50 | +Core API consists of Django apps with usual Django submodules like: |
| 51 | +- `apps.py` |
| 52 | +- `middleware.py` |
| 53 | +- `models.py` |
| 54 | +- `serializers.py` |
| 55 | +- `views.py` |
| 56 | +- `urls.py` |
| 57 | + |
| 58 | +We tend to add our own layers in the following modules: |
| 59 | +- `constants.py` for app-wide constant variables. |
| 60 | +- `dataclasses.py` for dataclass definitions, typically used for internal data transfer objects (DTOs). |
| 61 | +- `mappers.py` for data mapping logic unrelated to API requests and responses. |
| 62 | +- `services.py` for encapsulated business logic. Our goal with this layer is to make the views, models and serialisers leaner, so that the business logic is more clearly defined and easier to compose. |
| 63 | +- `tasks.py` for defining asynchronous and recurring tasks. |
| 64 | +- `types.py` for custom type definitions, including typed dicts. |
| 65 | + |
| 66 | +### Code guidelines: Flagsmith on Flagsmith |
| 67 | + |
| 68 | +To gate and gradually rollout features in the backend, we use the Flagsmith SDK in local evaluation mode: |
| 69 | + |
| 70 | +```python |
| 71 | +from integrations.flagsmith.client import get_client |
| 72 | + |
| 73 | +flagsmith_client = get_client("local", local_eval=True) |
| 74 | +flags = flagsmith_client.get_identity_flags( |
| 75 | + organisation.flagsmith_identifier, |
| 76 | + traits=organisation.flagsmith_on_flagsmith_api_traits, |
| 77 | +) |
| 78 | +ai_enabled = flags.is_feature_enabled("ai") |
| 79 | +``` |
| 80 | + |
| 81 | +To modify or add flags, edit [integrations/flagsmith/data/environment.json](integrations/flagsmith/data/environment.json), or run `poetry run python manage.py updateflagsmithenvironment`. |
0 commit comments