You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: api/README.md
+82-18Lines changed: 82 additions & 18 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -29,10 +29,91 @@ This codebase includes two kinds of tests:
29
29
30
30
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.
31
31
32
-
We recommend naming test functions using the `test_{subject}__{condition}__{expected outcome}` template, e.g. `test_get_version__valid_file_contents__returns_version_number`.
32
+
We enforce the `test_{subject}__{condition}__{expected outcome}` template for test names, e.g. `test_get_version__valid_file_contents__returns_version_number`.
33
33
34
34
We use the Given When Then structure in all our tests.
35
35
36
+
### Code guidelines: metrics
37
+
38
+
Flagsmith's backend exports Prometheus metrics. When planning a feature, consider which metrics should cover it — counters for domain events, histograms for latency or sizes, gauges for cardinalities. See [documentation for existing metrics](../docs/docs/administration-and-security/platform-configuration/metrics.md). Metrics code is hosted in `metrics.py` modules.
39
+
40
+
Name metrics `flagsmith_{domain}_{entity}_{unit}` and give them a comprehensive description.
41
+
42
+
### Code guidelines: logs
43
+
44
+
We use structured logging to mark up interesting operational and product events. Events emitted via structlog also flow through an OpenTelemetry pipeline and may be routed to a CDP or a data warehouse for product analytics.
45
+
46
+
When planning a feature, decide which moments deserve an event: things a product manager would ask about (an integration set up, a workflow committed, an import completed), or that a future oncall engineer would need to debug an incident. One well-shaped event per moment beats a wall of free-form `logging.info` calls.
47
+
48
+
```python
49
+
import structlog
50
+
51
+
# Use logger name as the event domain:
52
+
logger = structlog.get_logger("workflows")
53
+
54
+
# This will produce a `workflows.change_request.committed` OTLP log event
In your tests, verify your logs with the `caplog` fixture:
68
+
69
+
```python
70
+
from pytest_structlog import StructuredLogCapture
71
+
72
+
deftest_my_view__success__logs_expected(
73
+
log: StructuredLogCapture,
74
+
) -> None:
75
+
# Given / When
76
+
...
77
+
78
+
# Then
79
+
assert log.events == [
80
+
{
81
+
"level": "info",
82
+
"event": "action.succeeded",
83
+
"organisation__id": organisation.id,
84
+
}
85
+
]
86
+
```
87
+
88
+
Conventions:
89
+
90
+
- Logger name is the domain namespace — typically the app or package (`workflows`, `code_references`, `feature_health`).
91
+
- Event name is `entity.action` in snake_case (`scan.created`, `change_request.committed`). Do not repeat the logger name in the event, i.e `get_logger("saml")` with `"saml.configuration.created"` is redundant.
92
+
- Use double underscore to namespace event attributes, i.e. `namespace__property` will be emitted as `namespace.property`. Include the IDs of the entities the event is about (`organisation__id`, `project__id`, `environment__id`, `feature__id`) so events can be correlated with each other.
93
+
- Bind shared context once with `logger.bind(...)` rather than repeating attributes at every call site.
94
+
- Avoid PII — identify users and organisations by ID.
95
+
96
+
For errors, use `logger.exception(...)` or pass `exc_info=exc`, and keep the event name actionable (`import.failed`, not `error`).
97
+
98
+
### Code guidelines: feature flags (Flagsmith on Flagsmith)
99
+
100
+
To gate and gradually roll out features in the backend, we use the [OpenFeature](https://openfeature.dev/) SDK with a Flagsmith provider running in local evaluation mode:
101
+
102
+
```python
103
+
from integrations.flagsmith.client import get_openfeature_client
Organisations expose an `openfeature_evaluation_context` property carrying common traits — use it for org-scoped targeting. For other subjects, build an `EvaluationContext` with a stable `targeting_key` and the attributes your targeting rules need.
114
+
115
+
Add your feature as early as possible to the Flagsmith on Flagsmith project, and run the `updateflagsmithenvironment` management command to synchronise the local cache. You can use [Flagsmith MCP](https://docs.flagsmith.com/integrating-with-flagsmith/mcp-server) to integrate Flagsmith in your development flow.
116
+
36
117
### Code guidelines: migrations
37
118
38
119
To auto-generate migrations for your new code, run `make docker-up django-make-migrations`.
@@ -64,20 +145,3 @@ We tend to add our own layers in the following modules:
64
145
-`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.
65
146
-`tasks.py` for defining asynchronous and recurring tasks.
66
147
-`types.py` for custom type definitions, including typed dicts.
67
-
68
-
### Code guidelines: Flagsmith on Flagsmith
69
-
70
-
To gate and gradually rollout features in the backend, we use the Flagsmith SDK in local evaluation mode:
71
-
72
-
```python
73
-
from integrations.flagsmith.client import get_client
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