CARVIEW |
Select Language
HTTP/2 200
date: Sat, 11 Oct 2025 19:13:42 GMT
content-type: text/html; charset=utf-8
cache-control: max-age=0, private, must-revalidate
cf-cache-status: DYNAMIC
link: ; rel=preload; as=style; nopush,; rel=preload; as=script; nopush,; rel=preload; as=style; nopush,; rel=preload; as=script; nopush,; rel=preload; as=script; nopush
nel: {"report_to":"heroku-nel","response_headers":["Via"],"max_age":3600,"success_fraction":0.01,"failure_fraction":0.1}
referrer-policy: strict-origin-when-cross-origin
report-to: {"group":"heroku-nel","endpoints":[{"url":"https://nel.heroku.com/reports?s=eRdGTYet1hiGKc8iopZioRClaw1NnNMgM6%2FX%2FYn91S4%3D\u0026sid=e11707d5-02a7-43ef-b45e-2cf4d2036f7d\u0026ts=1760210021"}],"max_age":3600}
reporting-endpoints: heroku-nel="https://nel.heroku.com/reports?s=eRdGTYet1hiGKc8iopZioRClaw1NnNMgM6%2FX%2FYn91S4%3D&sid=e11707d5-02a7-43ef-b45e-2cf4d2036f7d&ts=1760210021"
server: cloudflare
strict-transport-security: max-age=0; includeSubDomains
vary: Accept,Accept-Encoding
via: 2.0 heroku-router
x-content-type-options: nosniff
x-permitted-cross-domain-policies: none
x-request-id: 74b84601-d5dd-8527-0a66-bb88abf40c74
x-runtime: 0.127812
x-xss-protection: 0
content-encoding: gzip
set-cookie: _secure_speakerd_session=fO1b219S%2BgNv3Xh2lJ%2FiXcl6QCyy4rcqo1d4KHzaGhYc0%2BwRABHx5RKNoBhcSCpu6ofEyiFKECKKfBTT4ldbGlZgXOIDj9Z1mfyAquy6MHwpxmdW5HR%2Bpl2x2%2FS8c8Co9docqGjl%2BlHfI9gzFaCeWrIp8DK9PBJapKhq3f7pUBOlvjz3AWY%2BV9yAEaQo6fJtGEO22QVc7TyXSv4eBCh9wOwfSQ7sf1%2FTHnOhRlewoyqrvkrgQWRWncnxS6XT4U2n6dtSdcB%2Fn1QvqHieiqMIkylS8RpdYW4v%2FfhM306j9L7rBpT2Qs6P49vw8B35fOQS51k42ceQVFj4ZdHKEtQUVhT8L84s1w76i0hBH6onSGBnK6nUPQxYow%2FqW1kUq%2F50KBKxzEfIZMf5tZDdkgcGZhrn--HaDyHleoq5AUyaUR--axeJgg98MNHggll3PR75jQ%3D%3D; HttpOnly; SameSite=Lax; Secure; Path=/; Expires=Sat, 25 Oct 2025 19:13:41 GMT
cf-ray: 98d0ad177cece8e0-BLR
Feature Flags - Speaker Deck
Feature Flags
A 15 minute presentation given at PyCon 2014 as part of the Advanced Django Patterns workshop https://lanyrd.com/2014/pycon/scxqhp/ describing feature flags, how we rolled our own at Lanyrd and how Eventbrite use Gargoyle.
Simon Willison
April 10, 2014
More Decks by Simon Willison
Other Decks in Programming
Featured
Transcript
-
class FeatureFlag(Model):! slug = SlugField(unique = True)! description = CharField(max_length
= 140)! enabled_for_all = BooleanField(default = False) -
class FeatureFlag(Model):! slug = SlugField(unique = True)! description = CharField(max_length
= 140)! enabled_for_all = BooleanField(default = False)! users = ManyToManyField(User)! -
class FeatureFlag(Model):! slug = SlugField(unique = True)! description = CharField(max_length
= 140)! enabled_for_all = BooleanField(default = False)! users = ManyToManyField(User)! groups = ManyToManyField(Group) -
def is_enabled(self, user):! return (! self.enabled_for_all or! self.users.filter(pk = user.pk).exists()
or! self.groups.filter(users__pk = user.pk).exists()! )! -
{% flag new_eventbrite %}! <p>Lanyrd is an <img src="/eb.png" alt="Eventbrite">
company</p>! {% endflag %}! -
In read_only_mode • Prevent logged-in user actions • Auth middleware
ignores user cookie • Mobile app APIs ignore user token • Shows the banner and hide the “sign in” link -
• User / Staff • Event • Organizer • API
key • Country • IP address (or range) • eventbrite.com v.s. .de v.s. .fr -
class RequestCountry(ConditionSet):! country = String(label='Country')! def can_execute(self, instance):! return isinstance(instance,
(HttpRequest,))! ! def get_field_value(self, request, field_name):! if field_name == 'country':! return getattr(request, 'country')! ! gargoyle.register(RequestCountry()) -
Gotchas • Unexpected flag combinations • Old feature flag code
• Performance! -
Gargoyle performance • Switch = DB row with JSON blob
of conditions • Caches switches in local memory AND remote cache • At Eventbrite, we run memcached on every frontend server to reduce network traffic -
• Use feature flags, not long-lived branches! • The ROI
on feature flags is ridiculous • Try Gargoyle (or Gutter), or roll your own