Trunk-based development and feature flags
Integrate continuously: keep branches short-lived, merge to main daily, and hide unfinished work behind feature flags.
- Explain why long-lived branches cause “merge hell” and slow delivery
- Practice trunk-based development with small, short-lived branches
- Use feature flags to separate deploying code from releasing features
At old Byte Bakery, each feature lived on its own branch for weeks. The “gluten-free menu” branch was 6 weeks old and 340 commits behind main when it was finally merged - which took three people two days, and broke checkout anyway.
Continuous integration originally meant a practice, not a tool: everyone integrates their work into the shared mainline at least daily, so conflicts are small and found early. Trunk-based development makes that the default:
- Everyone commits to
main(the trunk) directly or through short-lived branches that live hours, not weeks - ideally merged within a day. mainis always releasable. Every merge is built and tested, and a brokenmainis fixed (or the change reverted) immediately.- Bigger work is split into small, safe steps - and unfinished features are hidden behind feature flags.
Feature flags: deploy is not release
A feature flag (feature toggle) is a runtime switch around new code. The sourdough subscription feature can be merged and deployed every day while switched off for customers, then switched on for staff, then 5% of customers, then everyone - with no deploy. If it misbehaves, switching it off is the fastest rollback there is.
Flags come in kinds: release flags (temporary, for rollouts), experiment flags (A/B tests), ops flags (kill switches for expensive features), and permission flags (premium features). Percentage rollouts must be sticky: hash the user ID, so each user consistently sees the same thing.
1import zlib
2
3def is_enabled(flag, user, percent):
4 bucket = zlib.crc32(f"{flag}:{user}".encode()) % 100
5 return bucket < percent
6
7for user in ["ana", "gus", "bo"]:
8 print(user, [is_enabled("sourdough-subscriptions", user, percent) for percent in (10, 50, 100)])ana [True, True, True] gus [False, True, True] bo [False, False, True]
As the percentage grows, users only ever move from off to on: ana’s bucket is 9, gus’s is 33 and bo’s is 74, and those never change. (zlib.crc32 is used because Python’s built-in hash() of a string changes every time the program starts.)
Key takeaways
Integrate into main at least daily; long-lived branches create painful merges.
Keep main always releasable, and fix or revert a broken build immediately.
Feature flags separate deploying code from releasing it - and make rollback instant.
Use sticky, hash-based percentage rollouts, and remove flags when done.
Lesson quiz
7 questions · pass with 5 correct · up to 50 XP
Passing this quiz completes the lesson and keeps your streak going. Questions you miss come back in review sessions later.
Practice: automate DevOps chores in Python
Write the small Python tools DevOps teams really build - pipeline runners, plan checkers, metric calculators, scanners - and run them against sample inputs. They run locally in your browser; no servers or cloud accounts needed.
Branch health report
The first input line is today’s day number. Each following line is a branch: name created_day last_commit_day commits_behind_main. Give each branch a verdict, checking in this order:
stale: delete or reviveif there has been no commit for more than 3 days;too long-lived: merge behind a flagif it is more than 2 days old;rebase soonif it is more than 20 commits behind main;- otherwise
healthy.
Print branches oldest first (ties alphabetically) as gluten-free-menu (age 42): stale: delete or revive, then healthy: 2 of 5 branches.
- Byte Bakery branches
Python runs in a sandboxed browser worker with a 60 second time limit. Its runtime loads from the Pyodide CDN; your code stays in this browser.
Evaluate feature flags
The input has flags, ---, then checks. A flag line is name on|off percent optionally followed by allow:user1,user2. A check line is user flag.
Evaluate each check in this order and print the result with its reason:
- unknown flag:
ana dark-mode: off (unknown flag) - flag switched off (kill switch):
off (flag disabled) - user on the allowlist:
on (allowlist) - otherwise compute
bucket = zlib.crc32(f"{flag}:{user}".encode()) % 100:on (bucket 12 < 25)oroff (bucket 80 >= 25).
- Sourdough rollout
Python runs in a sandboxed browser worker with a 60 second time limit. Its runtime loads from the Pyodide CDN; your code stays in this browser.
Questions about this lesson
Stuck? Ask. Figured something out? Share it. Explaining is one of the best ways to learn.
Loading posts…