Skip to content

Conversation

@khvn26
Copy link
Member

@khvn26 khvn26 commented Dec 11, 2025

Contributes to #128.

In this PR, we add Python stdlib-based DynamoDB document schemas for Flagsmith's Edge API data model.

The goals are to:

  • Preserve a single source of truth for Edge API's data model.
  • Thoroughly document it, and uphold high documentation standards in the future.
  • Decouple the schema from its validation and serialisation code so the data can be consumed by typed Python code without having to rely on Pydantic dependency.
  • At the same time, make it usable for serialisation tools such as Pydantic and preserve compatibility with existing validation and serialisation behaviour.

Initially, I was hesitant to add validation code to this package. I'm considering to add it now, however, in a subsequent PR, so it can be consumed in environments where Pydantic is still possible to use.

I've added a bunch of TODOs that I expect to resolve prior to merging the current PR, and highlighted them by starting relevant review threads.

@codecov-commenter
Copy link

codecov-commenter commented Dec 11, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 95.17%. Comparing base (51c529b) to head (c743785).

Additional details and impacted files
@@                Coverage Diff                 @@
##           ci/uv-renovate     #137      +/-   ##
==================================================
+ Coverage           94.72%   95.17%   +0.44%     
==================================================
  Files                  76       79       +3     
  Lines                2444     2671     +227     
==================================================
+ Hits                 2315     2542     +227     
  Misses                129      129              

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These are placeholder types now. They might be annotated with optional validation code in the future.

"""Represents a multivariate feature state value assigned to an identity or environment."""

id: NotRequired[int | None]
"""Unique identifier for the multivariate feature state value in Core. TODO: document why and when this can be `None`."""
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems Core API currently stores both id and mv_fs_value_uuid. I'd like the field docstrings to explain why, but I need input from the team to do it properly.

CC @gagantrivedi @matthewelwell

Comment on lines +106 to +107
django_id: NotRequired[int | None]
"""Unique identifier for the feature state in Core. TODO: document why and when this can be `None`."""
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similarly to https://github.com/Flagsmith/flagsmith-common/pull/137/changes#r2620470807, we currently store both django_id and featurestate_uuid, and I'd like to document the order precedence and historical decisions here, if possible.

Comment on lines +313 to +314
django_id: NotRequired[int | None]
"""Unique identifier for the identity in Core. TODO: document why and when this can be `None`."""
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Apparently, this is None when identities are created by Edge. There's really no workaround for this so far; this is related to Flagsmith/flagsmith#2186.

Let me know if I'm missing anything, @matthewelwell @gagantrivedi.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe this answers your other two comments as well. If you're creating an identity override (for edge identity), the feature state ID will be None, and if that feature state is multivariate, the ID of the multivariate feature state value will also be None.

Copy link
Member Author

@khvn26 khvn26 Dec 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't this a bug then? I.e. I don't see why Core shouldn't have access to numeric IDs for feature states and multivariate feature states when an identity override gets created?

Is there an identity override creation path via Edge API I'm not familiar with?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not a bug - this is by design. When identity overrides are created via the Edge API, they never go through Django models/Postgres - they're only stored in DynamoDB. So they don't get a Django ORM primary key.

@khvn26 khvn26 marked this pull request as ready for review December 15, 2025 18:58
@khvn26 khvn26 requested a review from a team as a code owner December 15, 2025 18:58
@khvn26 khvn26 requested review from emyller, gagantrivedi and matthewelwell and removed request for a team December 15, 2025 18:58
@@ -0,0 +1,363 @@
"""
Types describing Flagsmith Edge API's data model.
The schemas are written to DynamoDB documents by Core, and read by Edge.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment is very confusing: The comment says "schemas are written to DynamoDB" - but schemas describe the shape of data, they aren't the data itself

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in 0179e00.

Comment on lines +313 to +314
django_id: NotRequired[int | None]
"""Unique identifier for the identity in Core. TODO: document why and when this can be `None`."""
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe this answers your other two comments as well. If you're creating an identity override (for edge identity), the feature state ID will be None, and if that feature state is multivariate, the ID of the multivariate feature state value will also be None.

@@ -0,0 +1,363 @@
"""
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment density here is quite high. Many of these docstrings just restate what the field name already tells us (name: str with "Name of the feature", enabled: bool with "Whether the feature is enabled"). Could we trim these down to only the non-obvious stuff - constraints, edge cases, gotchas?

Copy link
Member Author

@khvn26 khvn26 Dec 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I disagree. I'd like to keep the docstrings because even obvious field descriptions help to remove ambiguity, and having all fields documented help to support the discipline to document new fields going forward.

If anything, I'd add a linter that makes sure every field has a docstring in this module.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm gonna have to disagree here. If a field name needs a docstring to explain what it is, that's a sign the field name itself isn't good enough. The name and type should tell the story. Comments should explain why, not what.

The maintenance burden is real too - every field change now requires updating two places. Comments that just mirror the code tend to drift out of sync and become misleading over time.

Copy link
Member Author

@khvn26 khvn26 Dec 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comments should explain why, not what.

Sometimes the why and the what are the same, and sometimes they're different. The users of the code shouldn't have to guess. This is what I meant by removing ambiguity.

Comments that just mirror the code tend to drift out of sync and become misleading over time.

It's hard to imagine this scenario for me, given that document schemas are supposed to be forward compatible. Once the field is added, it stays. If its purpose changes or it gets deprecated, we only have to edit the docstring. Ideally, I don't see us changing the field names at all.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let’s open the floor for a wider discussion.: @matthewelwell @Zaimwa9

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fair points. I still think there's a distinction worth making though - if the what isn't clear from the field name, that's a naming problem. But I take your point about forward compatibility reducing the drift risk.

Maybe a middle ground: keep docstrings for fields where there's genuine context to add (constraints, defaults, deprecation notes, edge cases), but skip the ones that are purely restating the name? That way
we're not just adding noise for the sake of consistency.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do tend to agree with Gagan here.

As an example, this comment seems unnecessary to me, as I think Gagan already mentioned.

I'm happy for us to lean on the overly verbose side here, but I think this example is probably a little much.

@@ -0,0 +1,363 @@
"""
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The name of this module is pretty generic - doesn't tell us what these models are for. Something like edge_document.. or dynamodb_schemas.. would make the purpose clearer

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great suggestion! Renamed to flagsmith_schemas.dynamodb in ac9e8df.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants