[HN Gopher] The Twelve-Factor App (2011)
       ___________________________________________________________________
        
       The Twelve-Factor App (2011)
        
       Author : edward
       Score  : 317 points
       Date   : 2023-10-12 14:19 UTC (6 hours ago)
        
 (HTM) web link (12factor.net)
 (TXT) w3m dump (12factor.net)
        
       | ris58h wrote:
       | You should add 2017 in the title.
        
         | wesleytodd wrote:
         | yeah I got confused seeing this on the top. in my head said
         | "what is this, 2012?". Am I wrong this existed way before 2017?
         | 
         | edit: I am correct, it is much more old.
         | https://github.com/heroku/12factor/graphs/contributors
        
           | larsnystrom wrote:
           | First commit was 2011.
        
             | webmaven wrote:
             | The 12 factors go back even further, to Joel Spolsky's
             | "Joel Test" in 2000:
             | 
             | https://www.joelonsoftware.com/2000/08/09/the-joel-
             | test-12-s...
        
               | richardwhiuk wrote:
               | That's a rather different set of 12 factors.
        
       | screamingninja wrote:
       | Previous discussion-
       | https://news.ycombinator.com/item?id=31198956 (102 comments)
        
         | didntcheck wrote:
         | Another interesting thread about how these practices have held
         | up or changed over the past decade:
         | 
         | "Twelve-factor app anno 2022"
         | 
         | Article: https://xenitab.github.io/blog/2022/02/23/12factor/
         | 
         | Discussion: https://news.ycombinator.com/item?id=31225921
        
       | songshu wrote:
       | I love this generally but some of it has leaked out to the wrong
       | people and there are strong cargo cults around such things as one
       | codebase===one app.
        
       | notoverthere wrote:
       | Back in the early days of Docker, I did a whole bunch of work to
       | make WordPress behave as a Twelve-Factor App.
       | 
       | It traditionally hasn't behaved as one - which sort of makes
       | sense, because WordPress grew-up in a world where long-lived
       | servers with writable and persistent local disk storage was
       | commonplace.
       | 
       | I'm sure things have moved on since those days. This was back in
       | 2016. But it sure was a fun challenge!
        
         | DanielHB wrote:
         | I remember when I learned about making servers stateless by
         | storing session information in a database and just not writing
         | to disk and stuff. I was amazed at how much simpler it made
         | things and added the capacity to load-balance multiple nodes
         | without having to bother with session stickness.
         | 
         | Of course it made things harder in other ways like having a
         | separate DB for sessions.
        
       | frankfrank13 wrote:
       | I like this in general, but the number of times I've had to delay
       | a release because some non/psuedo-technical person has pulled out
       | "12 factor" like a universal yellow card has been enough for me,
       | overall, to ignore it almost completely. I guess the same has
       | happened to me for "agile" honestly. I recognize the intent of
       | guidelines like these, but they seem to be much more useful to
       | people whose only "value" is to provide ivory tower technical
       | leadership.
        
         | Waterluvian wrote:
         | I've had issues like that before. The solution was to have a
         | clear, documented process, and point them to that.
         | 
         | I've learned to take issues like that in good faith. Why are
         | they raising an issue? Is the existing process not clear? Is
         | there a deficiency in it? Is there a lack of reliability? If
         | reasonable concerns are found, the process can change (and the
         | documentation is to be updated).
        
           | ChrisMarshallNY wrote:
           | _> The solution was to have a clear, documented process, and
           | point them to that._
           | 
           | This has its own drawbacks. I worked for a company, in which
           | Process Was God, and Thou Shalt Do Only That Which God Has
           | Proclaimed Good.
           | 
           | It worked most of the time, but made it very, very difficult
           | to introduce flexibility and orthogonal approaches.
           | 
           | There's really no way to duplicate the results of
           | experienced, cohesive teams of high-functioning engineers,
           | with process. We can get similar results, in many ways, but
           | there's always a price to be paid.
           | 
           | T. A. N. S. T. A. A. F. L.
        
         | willseth wrote:
         | Give an example. Like most things, it's context dependent and
         | there are grey areas, but I think most devs use 12 factor like
         | a north star. I wouldn't block a release due to some minor
         | deviation, but generally lacking alignment should be treated as
         | tech debt as a minimum. If a release regresses significantly in
         | one of the factors, I think it's fair to block or at least
         | force more detailed review to understand why the dev felt the
         | tradeoff was worth it for that release.
        
           | cduzz wrote:
           | There's the "Plumber Problem[1]" by John Siracusa where you
           | watch a story and see a problem in your specific domain that
           | leads you to mistrust the whole story.
           | 
           | With the 12 factor nonsense, I see the guidance of "Dump
           | unformatted logs into stdout and make it someone else's
           | problem; they'll hadoop or splunk it and everyone wins."
           | There is no magic splunk that absolve you (the developer) of
           | generating useful data to logs, if you expect useful data out
           | of your logs. You can't reverse "cow -> burger -> sewage" and
           | shouldn't expect some magic hadoop to reverse entropy without
           | a ton of work.
           | 
           | So -- I guess the 12 factor app dictum is fine for some
           | trivial "I rewrote twitter in 80 lines of erlang" app, but in
           | my opinion it is a "if we can land a man on the moon we can
           | land a man on the sun" misstatement of complexity in actually
           | getting things done in the real world.
           | 
           | [1] https://hypercritical.co/2023/08/18/the-plumber-problem
        
             | morelisp wrote:
             | Spoken like someone who has never had to work on a Java
             | application with 22 custom log files, not including stdout,
             | stderr, and two separate syslogs.
             | 
             | Yes, if you want useful data from logs you'll need to log
             | useful data, but this is a case where 12 factor is so
             | successful / dominant today I don't think you even know
             | what it's arguing against anymore.
        
               | cduzz wrote:
               | No, writing files is nearly as bad as writing to stdout
               | -- both imply someone else has to parse the output.
               | 
               | If the logs matter -- write them to an API that retains
               | log structure by serializing it.
               | 
               | If the logs don't matter, then they don't matter, but
               | typically the logs are used for all sorts of analytics
               | processes after the fact.
               | 
               | Asking someone else to parse your data is rude.
        
               | nightpool wrote:
               | Again, "this is a case where 12 factor is so successful /
               | dominant today I don't think you even know what it's
               | arguing against anymore"
        
               | cduzz wrote:
               | The twelve factor app says: A twelve-factor app never
               | concerns itself with routing or storage of its output
               | stream. It should not attempt to write to or manage
               | logfiles. Instead, each running process writes its event
               | stream, unbuffered, to stdout. During local development,
               | the developer will view this stream in the foreground of
               | their terminal to observe the app's behavior.
               | 
               | That guidance is deeply foolish; I have to assume the
               | rest of the guidance is just as silly for anything but a
               | toy application.
        
               | lexicality wrote:
               | Where does it say "unformatted"? All it says is that your
               | logs should be sent to stdout.
               | 
               | You write your logs in whatever structure you want and
               | have something in the runtime environment (k8s, systemd,
               | file redirection in bash whatever) forward them to the
               | eventual storage location. I don't see the issue.
        
               | cduzz wrote:
               | So -- there's a file, that's created by some something in
               | a container emitting a string of bytes. A file must go
               | through a parsing and serialization phase to be useful to
               | a logging infrastructure. Just because you decorate the
               | contents of that file with { and } and other punctuation
               | doesn't make that file a string of json records, until
               | something's read and verified it.
               | 
               | I'm supposed to just load it into memory hoping it's json
               | or avro or whatever? Nope, errors happen; there's a limit
               | to the number of bytes that can be sent from one place to
               | another before the kernel may interrupt it (you're
               | writing an application log and your runtime emits it's
               | own debugging message; they get intermingled because
               | they're both being written to a fifo).
               | 
               | Maybe this is fine (usually it's fine) but eventually it
               | isn't fine. Use a logging library, categories your logs,
               | maybe start off with stdout but eventually put on some
               | big person pants and use a logging api and use the
               | stderr/stdout for high cardinality exceptions, not "all
               | my logs"
        
               | jameshart wrote:
               | 12 factor does not say 'don't use a logging library'
               | 
               | Use a logging library. Configure it to output well formed
               | and categorized log messages to stdout. Let the runtime
               | environment take that stream of data from there.
        
               | lexicality wrote:
               | Just so I understand, you are saying that the entire
               | concept of the 12 factor app is bogus because it tells
               | you to write unformatted logs to stdout because anything
               | you write to stdout is by its very nature unformatted
               | because even if you're outputting formatted data
               | something else in your program might decide to
               | concurrently write into your stdout buffer while you're
               | writing a log line and that will corrupt your entire data
               | stream irreparably?
        
               | jameshart wrote:
               | What 12 factor is telling you to do is to _not build a
               | log file rotator into your application_.
               | 
               | It is the least controversial and most broadly beneficial
               | of the 12 factors.
               | 
               | Why do you think it's foolish?
        
               | cduzz wrote:
               | It says "send it to stdout and let someone else figure it
               | out."
               | 
               | As that someone else, who often has bug reports like "why
               | aren't my log events all in one record in my thing?
               | Because you filled your stdout with newlines!" "Why did
               | my message vanish? It was important!" "because it's
               | malformed json because your runtime had an error; it's
               | over there instead of over here."
               | 
               | Programming's a stack of details; all of which matter.
               | Log using a logging API where available; use
               | stdout/stderr for exception logging only, if at all
               | possible. If you want your logging to be useful, think
               | carefully about how and what you log.
        
               | jameshart wrote:
               | That's a total misreading of what 12 factor is about.
               | 
               | All it means is to let someone else (other than the
               | program itself) figure out how to get the log messages
               | (whatever you decide they should be) from your process
               | (running wherever it is) to wherever you want to have
               | those logs for inspection.
               | 
               | Do not hardcode into your application the assumption that
               | they are written to a file or sent to a port.
               | 
               | In your application, write them to stdout.
               | 
               | In local dev, stdout will probably go to an interactive
               | console. In a container running in a cloud hosted cluster
               | it will probably go to a networked log aggregator that
               | collects it all into an indexed time oriented data store
               | along with other logs from other running instances.
               | 
               | Because you used stdout, that's easy to do.
               | 
               | If you wrote them to a file whose name your application
               | manages and changes itself to manage log rotation...
               | either of those is _hard_ to do.
        
             | jeremyjh wrote:
             | The reason people pay outrageous prices for Splunk is you
             | can get some pretty decent burgers out of sewage if you
             | know what you are doing, and from personal experience the
             | results can be totally game-changing if you are stuck with
             | a mishmash of different log formats and have to support a
             | large distributed application.
        
           | Aurornis wrote:
           | > Give an example.
           | 
           | I can give an example. The part about sticky sessions is good
           | generic advice for someone building generic web services, but
           | there are situations where sticky session-like functionality
           | or sharding-like behavior might be a better choice. Anyone
           | adhering literally to the 12-factor guide would get hung up
           | on trying to move that data to redis or memcached:
           | 
           | > Some web systems rely on "sticky sessions" - that is,
           | caching user session data in memory of the app's process and
           | expecting future requests from the same visitor to be routed
           | to the same process. Sticky sessions are a violation of
           | twelve-factor and should never be used or relied upon.
           | Session state data is a good candidate for a datastore that
           | offers time-expiration, such as Memcached or Redis.
           | 
           | The guide also has a strong preference for languages like
           | Ruby, Perl, and Python. There's even a quip about how
           | 12-factor prefers languages with a REPL, which in the wrong
           | hands becomes an argument against using Rust or other
           | languages.
           | 
           | People who think the entire world is simple CRUD web apps
           | tend to think that guides like this are completely universal.
           | It's a good guide for people making common web apps, but it's
           | not a perfect methodology for every backend service.
        
             | brodouevencode wrote:
             | > People who think the entire world is simple CRUD web apps
             | tend to think that guides like this are completely
             | universal
             | 
             | That's the very first point they make:
             | 
             | > In the modern era, software is commonly delivered as a
             | service: called web apps, or software-as-a-service. The
             | twelve-factor app is a methodology for building software-
             | as-a-service apps that...
             | 
             | Anything not web app/SaaS they express no opinion on.
        
             | willseth wrote:
             | I think it depends on how you interpret "rely". If your app
             | can gracefully recover to a reasonable state from a failure
             | to resume the expected session, then you could argue it's
             | not that reliant on it. Many systems use sticky sessions as
             | simply as an optimization, which is fine.
             | 
             | You didn't actually give an complete example. When are
             | sticky sessions a better choice than persistence?
        
         | robertlagrant wrote:
         | > but they seem to be much more useful to people whose only
         | "value" is to provide ivory tower technical leadership.
         | 
         | I've found it pretty useful in practice. I don't see why it's
         | much more useful to those people than it has been for me.
        
         | flakes wrote:
         | I call that weaponized dogmas. It's at least better than people
         | who insist on being DRY by writing an inscrutable generic
         | helper function which will only save 3 or 4 lines of code
         | duplication.
        
           | laurent_du wrote:
           | Nothing I hate more than a useless "helper" function that
           | wraps a built-in feature of the language and gets buried in a
           | "common lib" package that you need to inspect when you want
           | to understand what is going on, instead of just relying on
           | your knowledge of the language. Do we really need a function
           | to map a dictionary and a list of keys to the corresponding
           | values?
        
             | BossingAround wrote:
             | Oh my god I got a flashback to Groovy's missing method [0]
             | which caused us so many issues, and made our codebase so
             | unreadable.
             | 
             | When you discover it for the first time, you think, "huh,
             | neat, why not use it". Before you know it, nobody can
             | reason about your code and if you leave, nobody wants to
             | maintain it (esp. if your test suite is not extensive).
             | 
             | [0] https://groovy-lang.org/metaprogramming.html
        
             | booleandilemma wrote:
             | I'm a huge hater of these and I think they almost always
             | decrease readability of the code and slow down the
             | development and review process.
             | 
             | I want to read and write the language I'm developing with,
             | not the helper methods of some overeager developer who
             | wanted to leave his mark on the codebase.
        
           | brodouevencode wrote:
           | The absolute counter to weaponized dogma is itself weaponized
           | dogma. There are times when implementing DRY, 12FA make sense
           | and times when it doesn't. Be careful with hard-lined
           | stances.
        
         | samtho wrote:
         | This is more a reflection of the organizational structure that
         | allows or tolerates such behavior rather than anything to do
         | with the 12 factor app methodology. I think most reasonable
         | people would agree that if you were able to incorporate most of
         | the list, you're doing alright.
        
         | Aurornis wrote:
         | > some non/psuedo-technical person has pulled out "12 factor"
         | like a universal yellow card has been enough for me, overall,
         | to ignore it almost completely.
         | 
         | Ignoring it completely on principal is just as bad as the
         | people who treat it like mandatory dogma.
         | 
         | I've also had over-eager junior engineers or wannabe architects
         | use the 12-factor article as mandatory requirements for any
         | launch. It takes a firm and unified explanation that these are
         | good goals to strive for, but in the real world we need to make
         | compromises for launch and choose which aspects to delay or
         | defer.
        
           | lopatin wrote:
           | Ignoring it completely is a viable strategy. A lot of 12
           | factor sounds like common sense to people who regularly ship
           | stuff. For example, if I fail to put the config in the
           | environment, the risk that introduces will be immediately
           | obvious in the PR and someone would be able to raise that
           | concern, with real justification, not because it's declared
           | in a 12 factor manifesto.
        
             | i-use-nixos-btw wrote:
             | I think ignoring it completely is a viable strategy once
             | you've already gone through the stages - when your mind is
             | in the right place.
             | 
             | I have moved on a long way from the days that 12 factor was
             | my bible, but those days completely redefined my way of
             | developing software. I'm not exaggerating here - it
             | completely shifted my approach.
             | 
             | I guess it's similar to Scrum in that way - it isn't a
             | great end point, but if you're stuck in the old ways (i.e.
             | the methods used before it was even a thing) then it's at
             | least a good approach to sink your teeth into, try it out,
             | shape your approach, and move on from there.
             | 
             | Basically, I'd rather see a replacement that improves on 12
             | factor than throwing out the book entirely.
        
             | ascendantlogic wrote:
             | > A lot of 12 factor sounds like common sense to people who
             | regularly ship stuff.
             | 
             | In 2023, yes. A lot of the 12 factor methodologies became
             | table stakes in the modern cloud era. Back when this was
             | written "cloud" was not nearly as ubiquitous as it is now,
             | although we were certainly headed in that direction. A lot
             | of the comments here are viewing this through a 2023 lens
             | and forgetting it was written in 2011.
        
         | darkwater wrote:
         | Well, if your app log directly to files on disk (extra bonus if
         | it doesn't respect any verbosity setting) or will only works if
         | secrets are hard coded in config files (even if this could be
         | work-arounded at deploy time probably), I for one will do all
         | that I can to avoid your app reaches production.
         | 
         | Yeah, I work in ops^W platform.
        
         | brodouevencode wrote:
         | No amount of dogma should interfere with a deployment within
         | the scope of an MVP. Once it's out of MVP then anything not
         | addressed best practice-wise is tech debt. If your organization
         | had adopted 12FA as a set of best practices then those should
         | be met, but they should never interfere with a deployment.
         | 
         | 12FA is not a single box that is to be checked. It can and in
         | most cases probably should be implemented over time as the
         | product matures, taking each point, point by point (and maybe
         | even breaking those up) and adding that into the product. If
         | you've done good engineering up front (abstractions and
         | interfaces where it makes sense, not hard coding everything)
         | this should not be an issue.
         | 
         | inb4 YAGNI: this is just as abused as 12FA.
         | 
         | EDIT: the biggest thing 12FA is missing are concrete examples
         | that junior engineers can reference.
        
         | jsight wrote:
         | TBH, most of the value in things like this is for consultants
         | who go into shops that really don't know better.
         | 
         | Being able to point management at this can be really useful.
         | 
         | For most of us here, it is 99% common sense.
        
         | bunderbunder wrote:
         | Indeed.
         | 
         | Hard to believe that treating best practices as strict rules
         | would not turn out to be best practice, isn't it?
        
       | ireallywantthat wrote:
       | Nowadays, I see many articles titled "Beyond 12 factor Apps"
       | which proposes even more factors for App prominent among them
       | being one from IBM. What's your opinion on that?
       | 
       | https://developer.ibm.com/articles/15-factor-applications/
        
         | Moto7451 wrote:
         | This is a small but real trend. My experience at a company that
         | went deep into expanding beyond/removing some of the 12 Factors
         | is that it breaks portability of containerized software built
         | with those assumptions. There were a few times I needed to
         | install a vendor product or open source project that was built
         | to the 12 factors but spent a month or more just building a
         | shim to make it conform to our particular bend on it. I'm sure
         | there are good reasons to deviate but I wasn't particularly
         | impressed with our decisions and more importantly why we made
         | them.
        
       | aj_g wrote:
       | For sure an influential engineering codex. Feels almost strange
       | thinking about all the easy abstractions we have now for hosting,
       | like Render or Vercel or that this was written in 2012, when web
       | apps were much much more of a wild west in terms of
       | accepted/shared practices.
        
       | RugnirViking wrote:
       | I was kinda expecting this from the title to be some sort of
       | commentary on two factor authentication, like an app that needs
       | passport photo, face scan, drivers licence, sms text, google
       | authenticator, email link, password, fingerprint, all together to
       | log in just once.
        
         | patrickaljord wrote:
         | Your average centralized crypto exchange.
        
         | moffkalast wrote:
         | Sounds like roughly what the average stock or crypto exchange
         | requires before they let you trade haha.
        
           | rando_dfad wrote:
           | Thank KYC/AML for that.
           | 
           | Money is not data, it is a complex social construct. Moving
           | money requires interacting with that social construct, i.e.
           | government regulations.
           | 
           | Yes, crypto was supposed to get around that. No, government
           | didn't agree.
        
             | moffkalast wrote:
             | Well I'm glad they at least aren't collecting skin samples
             | yet.
        
       | revskill wrote:
       | Sorry, i put all secrets in my code. I don't need 12 factors at
       | all.
       | 
       | It's super easy to deploy to multiple environments, because code
       | is single source of truth.
        
         | Aurornis wrote:
         | Secrets in code is bad practice because it puts the secrets in
         | source control and gives them to anyone with access to the
         | code.
         | 
         | You separate out the secrets to keep them secret.
        
           | revskill wrote:
           | Next evolution of Git should automatically introspect the
           | code base for comment to leaving out all "secrets".
           | 
           | My point is, make the code easy to clone in a runnable shape
           | wihtout any hacking on host system.
        
             | gdprrrr wrote:
             | You can configure GitLab to reject the push if you comitted
             | secrets, based on regex marching with common formats for
             | tokens.
        
         | pjs_ wrote:
         | Hell yeah brother. Move fast and break things philosophy
        
         | porsager wrote:
         | It's not a problem. Here you go: https://github.com/AGWA/git-
         | crypt
        
           | samus wrote:
           | This only works if secrets are frequently rotated and contain
           | enough entropy. Else, all secrets will eventually be
           | decrypted from leaked repositories and distribution
           | artifacts.
        
         | brodouevencode wrote:
         | What company do you work for? I'd like to cancel my
         | subscription.
         | 
         | Seriously...bad idea. One repo leak or even one bad copy/paste
         | in StackOverflow and you're done.
        
       | ACV001 wrote:
       | Boring. This is already happening in any sane modern application.
        
         | seanhunter wrote:
         | It might be worth you reading
         | https://news.ycombinator.com/newsguidelines.html
         | 
         | In particular:                  > Be kind. Don't be snarky.
         | Converse curiously; don't cross-examine. Edit out swipes.
         | > Please don't post shallow dismissals, especially of other
         | people's work. A good critical comment teaches us something.
        
         | gouggoug wrote:
         | > This is already happening in any sane modern application.
         | 
         | Probably in part thanks to 12factor.net
        
       | sbjs wrote:
       | I genuinely feel like there's two camps of professionals: those
       | who know how to do something and do it extremely well; and those
       | who know how to describe something and break it down very well.
       | The former are the ones making the magic happen, like Fabrice
       | Bellard. The latter become very influential teachers like Bob
       | Martin. Both are needed (although the former more so), but the
       | latter usually get all the attention and credit.
        
       | webscalist wrote:
       | I prefer to have known configuration presets (groups) version
       | controlled. Documents known shared environments (db host, ...
       | etc) well.
        
       | vilunov wrote:
       | I feel like each of this points can be reasonably disputed.
       | 
       | 1. One app - one repo. There is nothing fundamentally wrong with
       | multiple apps (as in separate pods/containers/processes) being
       | developed in a single repo, as long as they are tightly coupled
       | functionally, share the release cycle, but still need to be
       | deployed separately to get advantages of separate processes and
       | independent scaling. The first example that comes to mind is
       | separation of public api and worker processes, e.g. ruby's
       | sidekiq, python's celery or a generic kafka consumer.
       | 
       | 2. > A twelve-factor app never relies on implicit existence of
       | system-wide packages.
       | 
       | This is incredibly hard to achieve in practice if you're not
       | running something nix, and even then there are leaks of
       | dependencies on kernel syscall APIs. Every Rust app implicitly
       | depends on a glibc (except musl ones), which is present in major
       | Linux distros except slim ones such as Alpine. In fact, the whole
       | Docker crutch IMO is needed specifically to alleviate this
       | problem.
       | 
       | 3. > The twelve-factor app stores config in environment variables
       | 
       | It seems to me much more brittle, as it forces you to store
       | secrets in envs, which could be less secure, and to abandon
       | structured file configs, which may enjoy type safety, IDE
       | autocomplete and automatic parsing. With envs you are forced to
       | implement parses for non-trivial and non-string config vars
       | yourself. In fact, envs are often committed into repos as well in
       | form of dot-env files, so the point about commit-safety is moot
       | as well.
        
         | twic wrote:
         | > This is incredibly hard to achieve in practice if you're not
         | running something nix, and even then there are leaks of
         | dependencies on kernel syscall APIs.
         | 
         | You're misinterpreting that guideline. The text says (my
         | emphasis):
         | 
         | > Most programming languages offer a packaging system for
         | distributing support libraries, such as CPAN for Perl or
         | Rubygems for Ruby. Libraries installed through a packaging
         | system can be installed _system-wide_ (known as "site
         | packages")
         | 
         | It's not saying not to depend on the operating system (of which
         | glibc is part), it's saying not to require particular language
         | package manager packages to be installed on the machine.
         | 
         | Agreed that the other ones you mention are silly, though.
         | Especially the one about environment variables - that isn't
         | actually reasoned advice, it was just the authors writing down
         | what was most common in Ruby development at the time, on the
         | assumption that that must be the best thing to do.
        
           | vilunov wrote:
           | Well, glibc could be considered part of the operating system,
           | although this too is arguably not correct, because some
           | technologies go out of the way to not depend on anything
           | present in the system and be completely self-contained, e.g.
           | fully statically linked Go binary with no Cgo usage.
           | 
           | Nevertheless, glibc is not the only system dependency which
           | is often used by apps, another such example is JVM of a
           | specific version, which is not tracked by any of the popular
           | JVM build tools and package managers; and OpenSSL, which is
           | too installed by the system package manager.
           | 
           | Dockerfiles exists to fix this by defining all the necessary
           | installation steps as a sequence of instructions, but this is
           | far from a proper package manager.
        
           | aniforprez wrote:
           | In my experience, sourcing config from env variables seems
           | like the best thing to do because you can completely ignore
           | where the app is running in favor of injecting whatever you
           | want in any environment of your choosing and your app no
           | longer needs to care about that. The implication is that
           | business logic must not include the content of your config or
           | where it is sourced from. It must simply use the values in
           | the config. This way my apps could give less of a fart if I
           | run it as a container in kube, in nomad, in local on
           | localhost, in a devcontainer, in a directory with vars loaded
           | using direnv, in vercel/netlify and so on. This also allows
           | incredible flexibility in how that config is sourced as long
           | as you simply dump them into the env vars. Want to load
           | secrets from a .env file on local? Sure. Want to load secrets
           | from Vault on production? Run a simple script to load vault
           | secrets onto the env and run your app.
        
             | dalyons wrote:
             | yeah this is the actual point - it makes the code/app
             | totally portable & decoupled from config. I find it
             | significantly better than having to have the app code
             | choose different config based on interrogating facts about
             | its runtime (eg am i in prod? use this value. am i in test?
             | use this)
        
           | jjice wrote:
           | I heavily agree. I used to work on a LAMP stack where the
           | deployment was just dumping the code into an EC2 instance.
           | All is fine until there's a weird system dependency error
           | that isn't documented because the AMI being used has just
           | been hand built over the last three years. Have fun tracking
           | down that specific PHP system dependency when AL2 goes EOL. I
           | don't want to write code that doesn't have _documented_
           | dependencies on system packages. Even just a Dockerfile
           | serves as solid documentation on what we're using.
        
         | damianh wrote:
         | "One app - one repo"
         | 
         | Where did you see that being expressed?
        
           | vilunov wrote:
           | https://12factor.net/codebase
        
             | ulizzle wrote:
             | It says there that a codebase can contain multiple repos.
             | They don't mean a monorepo when they say codebase
        
               | vilunov wrote:
               | > A codebase is any single repo (in a centralized
               | revision control system like Subversion), or any set of
               | repos who share a root commit (in a decentralized
               | revision control system like Git).
               | 
               | They do mean that a repo = a codebase. A remark about
               | "set of repos" is just a technicality of distributed
               | VCSs, meaning that each copy cloned from a single source
               | (or from other copies) is a single repo.
        
         | samus wrote:
         | I agree with your view of the first rule and would gladly store
         | multiple related apps in one repo. No rationale for why one
         | repo should only containing one app is given, therefore that
         | rule can be disregarded.
         | 
         | Dynamic or managed languages can often ignore most of the
         | hassle with system packages.
        
         | brodouevencode wrote:
         | > 1. One app - one repo.
         | 
         | There is a U-shaped utility to monorepos. Most projects fall in
         | the middle somewhere, largely ones managed by independent
         | engineering teams that have no centralized
         | platform/devops/whatever-we-are-calling-it-these-days team.
        
         | kagakuninja wrote:
         | They also say every server should be a separate process, which
         | is debatable. When using languages that lack real threads
         | (Ruby, Python, etc) you have no choice.
        
         | talent_deprived wrote:
         | > There is nothing fundamentally wrong with multiple apps (as
         | in separate pods/containers/processes) being developed in a
         | single repo, as long as they are tightly coupled functionally,
         | share the release cycle, but still need to be deployed
         | separately to get advantages of separate processes and
         | independent scaling.
         | 
         | We disagree on fundamentals then and I think more of the world
         | agrees with me and with the 12 factors.
        
       | twic wrote:
       | The big thing missing from this document is justifications for
       | the rules. It's almost all just rules. Are the rules good? Hard
       | to say, and this document won't help you find out.
        
         | brodouevencode wrote:
         | If you click on the rule it links to a page that is what you're
         | asking for. For example: https://12factor.net/backing-services
        
           | samus wrote:
           | Not all the rules are explained on. For example, a detailed
           | reasoning why there should be only one app per repository is
           | not provided. It is only stated that it is not the 12factor
           | way.
        
           | twic wrote:
           | The page you link to does not contain a justification for the
           | rule.
        
       | moondev wrote:
       | Kubernetes basically forces you to deploy 12fa, using these
       | guidelines is a pretty good playbook for migrating an existing
       | application as well.
        
       | sergioisidoro wrote:
       | Twelve factor app: use environment for config
       | 
       | Docker: Don't use environment for config, it's insecure
       | 
       | I like and use many of the patterns of 12 factor, but indeed some
       | of these were written in the context of VPS, where environment is
       | stable, secure and more constant (unlike containers, where
       | environment might end up shipped in one of the layers). In the
       | age of containers, this particular point has been a bit of a
       | struggle for me. Docker secrets don't always play well, and you
       | need to do some gymnastics to make it work.
        
         | samus wrote:
         | The basic idea is still valid. You might just have to use a
         | more appropriate mechanism when using containers. For example,
         | the explanation of the Config factor explains that a config
         | file can also be used.
        
         | 3by7 wrote:
         | In retrospect, a better name for that section could have been
         | "Separate config from code" as it says in one of the first
         | paragraphs.
        
         | chrissoundz wrote:
         | How are environment variables insecure?
        
           | dharmab wrote:
           | Envvars have much weaker access controls than files;
           | basically anything in the same PID namespace can read your
           | envvars. Poke around in /proc/*/environ to see for yourself.
           | Files can use user permissions and MAC (AppArmor, SELinux,
           | etc) to secure them against unauthorized processes.
        
             | wutwutwat wrote:
             | If running in a containerized environment ENV is injected
             | only to the container, at container runtime. If you're
             | running your containers with a single process, you are
             | doing the exact access control you just outlined. At that
             | point what is the difference between a file on disk with
             | read permissions for the proc owner vs only the proc having
             | the ENV injected to its shell at runtime? I'd maybe even
             | say the ENV at runtime is _more_ secure than the chown'd
             | file on disk because the ENV injected variant is ephemeral
             | and when that container /proc dies, so does the ENV sitting
             | in its memory, whereas the other leaves a file on disk
             | potentially depending on where it was written to,
             | regardless of who can access it. Then there's the fact that
             | a lot more people than you think run containers as root so
             | all file access control goes out the window.
             | 
             | From a security perspective it is always more secure to
             | have something only in memory of the running process than
             | it is to have a file on disk regardless of file permissions
        
               | dharmab wrote:
               | Containers don't enforce that isolation. Another process
               | can nsenter the container's PID namespace. You also have
               | issues if your container's PID1 creates subprocesses.
               | 
               | File are not necessarily written to disk. e.g. the
               | Secrets CSI Driver loads secrets directly from a secrets
               | store as virtual files within ephemeral volumes.
        
         | jcrites wrote:
         | Could you elaborate? How are environment variables insecure? If
         | the secret will be present in the application throughout it's
         | lifetime, then what's a better alternative?
         | 
         | Just because a secret is injected into the application as an
         | environment variable does not mean it's handled insecurely
         | elsewhere. For example, AWS ECS containers have built-in
         | support for fetching secrets from Secret Manager at startup and
         | passing them as environment variables:
         | https://docs.aws.amazon.com/AmazonECS/latest/developerguide/...
         | 
         | This fetches the secret at the time the container starts up
         | (IIRC), and uses the IAM credentials of the running application
         | to fetch them from Secret Manager (so it must have permission
         | to the secret).
         | 
         | The merit of using environment variables seems to depend
         | entirely on how they are, well, configured. With a mechanism
         | like this I don't see much disadvantage. (The main disadvantage
         | I see is that generic malware that attempts to dump environment
         | variables might capture them, but defending against that threat
         | is largely obfuscation, unless you aren't storing the secret at
         | all permanently in memory.)
        
           | dathinab wrote:
           | - any sub process has full unlimited access to env variables
           | 
           | - env variables are prone to be dumped in various contexts
           | and might leak secrets to e.g. logs
           | 
           | - env variables might leak, e.g. in context of docker build
           | stages etc. (but then using building a docker image to build
           | both the image and the software is often anyway not a good
           | idea)
           | 
           | - env variables are often easily accessible by other
           | processes
           | 
           | the method you mentioned of ad-hoc injecting a secret as env
           | by some form of security manager for containers avoid many
           | such problems
        
             | lazyant wrote:
             | > - env variables are often easily accessible by other
             | processes
             | 
             | A process would need to have the same permission (same
             | user) or root to read /proc/$PID/environ no? or what
             | mechanism is there for a process to read another process
             | env vars (not children processes, that was already
             | mentioned)? unless of course, a process dumped them in some
             | world-readable file (like logs as mentioned) or some other
             | silly way
        
               | ezekg wrote:
               | Exactly. The operating system already offers security for
               | environment variables: use it. And you get the benefit of
               | increased security in other places by not sharing users
               | between applications.
        
             | yjftsjthsd-h wrote:
             | > env variables might leak, e.g. in context of docker build
             | stages etc. (but then using building a docker image to
             | build both the image and the software is often anyway not a
             | good idea)
             | 
             | Since the rule describes runtime configuration, it
             | shouldn't be involved with the build process at all
        
           | sergioisidoro wrote:
           | My comment is specific to docker, although environment as a
           | broader sense can be discussed.
           | 
           | I remember reading that it is possible to end up in
           | situations where your environments can end up in the layers
           | of the docker image. Also, you can read the secrets of a
           | container with inspect, and apparently linked containers
           | might be able to access other containers's secrets.
           | 
           | I think it was this article: https://snyk.io/blog/keeping-
           | docker-secrets-secure/
        
           | derefr wrote:
           | Presumably because /proc/$PID/environ exists and can be read
           | by the user that owns the process -- which means that any
           | attacker that can figure out how to spawn an arbitrary
           | subprocess running as the daemon user (a pretty common
           | vulnerability class!) can exfiltrate all your env-vars.
           | 
           | It's similar to the reason that you're not supposed to supply
           | passwords on the command-line (/proc/$PID/cmdline exists),
           | but instead either read them from stdin, or do the big dance
           | of
           | 
           | 1. creating a file that contains the password, is owned by
           | root, and is 0400;
           | 
           | 2. having your daemon start off running as root for just long
           | enough to read the password into process memory;
           | 
           | 3. then having your daemon drop privileges before doing any
           | work, so that someone exploiting the daemon _won 't_ have the
           | privilege as the "do the work" user to read the file with the
           | password in it.
        
         | dathinab wrote:
         | it's both true
         | 
         | putting any form of secrets into a env var is deeply insecure
         | 
         | using env is the most simple uniformly available way to have
         | configs
         | 
         | through I would take it a step further, anything which must not
         | be leaked probably should not be in any config, weather env or
         | file or similar, it should be injected through other means if
         | viable
        
         | 0cf8612b2e1e wrote:
         | On the other hand, without platform support there are not a lot
         | of easy alternatives. It at least encourages separation of
         | config and code, keeping secrets out of vcs.
        
         | wutwutwat wrote:
         | One could argue that you should never consider any input as
         | "safe to consume", and if you write defensive app code, would
         | never consider the ENV to ever be secure or what you expect,
         | and would therefor sanitize that input before consuming. If
         | you're blindly trusting any input just because you're single
         | tenant, you're in for some hard to track down bugs and long
         | nights when those circumstances one day change due to random
         | business pivots.
        
         | jsight wrote:
         | Yeah, I've never been a fan of secrets in env vars for this
         | reason. It is shocking how frequent dumb debug consoles have
         | accidentally exposed env vars. You really don't want that to
         | accidentally expose your private keys.
         | 
         | There's really nothing wrong with secrets mounted on the file
         | system via k8s. But, of course, all of this depends on your
         | deployment environment.
         | 
         | Sometimes env vars are the least bad option, because secrets
         | really are always hard.
        
           | berkes wrote:
           | > dumb debug consoles have accidentally exposed env vars
           | 
           | Huh? If the app can read the secret, then the app can... read
           | the secret. Maybe I'm misunderstanding what you call a "debug
           | console", but if this is anything running in the app, it can
           | ... read the secrets. Regardless of their source.
           | 
           | Or, to put it differently: it matters nothing if your secrets
           | at rest are stored in a quantum-resistent-encrypted vault,
           | secrets.yml or ENV vars, if your logging, backtracing or
           | debug consoles dump them somewhere, you are compromised
           | regardless.
        
             | jsight wrote:
             | This is absolutely true, but it is far more common for
             | someone to accidentally leave open a display of env vars
             | than a display of files.
             | 
             | I've got this problem to debug in prod, I know, I'll embed
             | the env vars in the source code of the error page. No big
             | deal and it might help me debug that! For a dev used to not
             | using secrets in env vars (for example, when testing
             | locally), this is an easy mistake. He may even use a third
             | party library that embeds features like this without
             | knowing it! Even things like the jmx console can be risky,
             | since access to that may not relate 1:1 with access to
             | secrets.
             | 
             | I've never seen anyone make a similar mistake and
             | accidentally embed all_my_passwords.ini in their output.
             | 
             | I must admit that some of what I've said above has become
             | outdated, since it is now a much more common practice to
             | embed passwords in env vars than it used to be.
        
       | zemo wrote:
       | the twelve-factor app is a set of recommendations from 2011 that
       | are based less on engineering principles and more on the
       | capabilities of Heroku and containerized infrastructure in 2011.
       | For example:
       | 
       | > Another approach to config is the use of config files which are
       | not checked into revision control, such as config/database.yml in
       | Rails. This is a huge improvement over using constants which are
       | checked into the code repo, but still has weaknesses: it's easy
       | to mistakenly check in a config file to the repo; there is a
       | tendency for config files to be scattered about in different
       | places and different formats, making it hard to see and manage
       | all the config in one place. Further, these formats tend to be
       | language- or framework-specific.
       | 
       | > The twelve-factor app stores config in environment variables
       | (often shortened to env vars or env).
       | 
       | yeah the reason they argue for this is that the people that wrote
       | this worked on Heroku, and the way that Heroku worked is you
       | populated the environment variables from some fields in a web
       | app. If you want your config history tracked in version control,
       | or you do gitops, or you have k8s configmaps, or you want to have
       | your configuration files on a mounted volume ... those things are
       | all broadly fine; they keep the configuration state separate from
       | the app deploy state. This document really confuses the forest
       | with the trees and recommends things based less on actual
       | engineering principles and more on the product capabilities of
       | the corporation that produced it. It is an actively harmful set
       | of guidelines.
        
         | morelisp wrote:
         | The 12 factor app that stored config in env vars found it
         | trivial to migrate to ConfigMaps. The LOB apps today that
         | expect complex ConfigMap layouts (at worst case, directly from
         | the k8s API) are going to be hell to migrate to whatever comes
         | after k8s.
        
         | throw1234651234 wrote:
         | The whole .env file holding all your configs thing is a
         | disaster. People forget to update the template, it has to be
         | passed around, etc.
         | 
         | Configure your secrets in Azure Key Vault (AWS Secrets Manager,
         | GCP-something-I-forget) or whatever the non-cloud Vault
         | software is called.
         | 
         | Devs only set client_id, client_secret, and env defaulted to
         | dev. On startup, the app fetches configs for the secret store.
         | The needed secrets are in source control. If the needed secret
         | is missing from the secret store, there is a clear error on
         | startup.
         | 
         | You are welcome, I just saved you weeks of onboarding,
         | confusion, and devs passing around .envs on Slack.
        
           | campbel wrote:
           | I agree, sops / ejson / gitcrypt or something else that
           | encrypts a file in vcs, then use your cloud or shared secret
           | vault to disperse the decryption key.
        
           | danwee wrote:
           | > You are welcome, I just saved you weeks of onboarding,
           | confusion, and devs passing around .envs on Slack.
           | 
           | Don't get it. At one company I worked, .env files for the DEV
           | environment were open (the file itself was stored on Vault,
           | so only engineers had access to it), and one could only
           | access DEV resources via VPN. So, starting a service using
           | the DEV .env file was rather easy an uncomplicated.
        
             | throw1234651234 wrote:
             | Here is the problem with that. A dev pushes a change that
             | requires a change to the env file. Another dev pulls down
             | the code, but doesn't change the env file. Everything
             | breaks. At really bad companies, DevOps sets env vars from
             | somewhere other than that file passed around by devs -
             | another failure point.
             | 
             | My proposal:
             | 
             | 1. Key Vault | HashiCorp Vault | whatever is the source of
             | truth for secrets.
             | 
             | 2. When a developer adds a key, they add it to Key Vault
             | and update code to use it.
             | 
             | 3. On app startup, code checks the dev's local for 2 env
             | vars ONLY, which allow access to key vault.
             | 
             | 4. The app then tries to pull in all the env vars it needs
             | by secret name.
             | 
             | 5. If any are missing, it fails.
             | 
             | This keeps devs from having to pass around .env files
             | outside of source control. Btw, Azure (and all other cloud)
             | builds can be configured to pull env vars directly from Key
             | Vault, so there's that benefit too.
        
               | parasti wrote:
               | "5. If any are missing, it fails."
               | 
               | That's literally all you need to solve the scenario you
               | described with .env files.
        
               | meowtimemania wrote:
               | The difference is any time the .env config is updated,
               | all engineers have to update their .env file.
               | 
               | So with .env file:
               | 
               | - developer adds to .env
               | 
               | - pushes code
               | 
               | - all developers see failures in dev env due to missing
               | env var
               | 
               | - all developers search slack/email to figure out what
               | went wrong
               | 
               | - all developers update their .env file and things are
               | working again
               | 
               | --------- In gp's comment -------
               | 
               | - one person works on feature that introduces new config
               | 
               | - failure happens in their local environment
               | 
               | - they update config backend and push code
               | 
               | - all other developers get updates with no changes needed
        
               | throw1234651234 wrote:
               | Exactly, thank you for phrasing it much better than I
               | did!
        
               | Rapzid wrote:
               | You can also just supply defaults that can be locally
               | overriden.. Benefit is they are reviewable in the same PR
               | that adds the code depending on them..
        
           | nickzelei wrote:
           | I've resulted into a pretty similar setup.
           | 
           | We use SOPS and the encrypted .env file is stored in VCS. The
           | key is stored in 1password with instructions on where to
           | stuff it on your machine. Then you just decrypt the .env file
           | and you're off to the races.
        
             | edwinbalani wrote:
             | For anyone new to SOPS like I was -
             | https://github.com/getsops/sops
        
           | victorNicollet wrote:
           | Hard agree. I think it's easy to get stuck on the mental
           | model that "the configuration value is the secret" (which
           | means the configuration itself needs special handling), and
           | the correct mental model should be "the configuration value
           | is the identifier of the secret", with another system (Azure
           | Key Vault, etc) being responsible for converting the secret-
           | identifier into the secret-value.
           | 
           | My company open sourced the .NET library we use for this:
           | https://github.com/Lokad/Secrets
        
           | geraldwhen wrote:
           | The corporate VPN and proxy setups tend to fail or be slow.
           | This would add non-trivial startup time to any app, and
           | catastrophic startup time to integration tests.
        
         | jbotdev wrote:
         | It is true that this is somewhat influenced by how Heroku
         | works, but ConfigMaps and GitOps do not meet the same security
         | and usability requirements as Heroku config/env vars.
         | 
         | If you want secure config storage on Kubernetes, you end up
         | using Secrets, which ends up being key-value like env vars
         | anyway. If you want similar security with Git you need a layer
         | of encryption, which breaks diffs and requires additional
         | tooling. This all leads back to why high-level deployment tools
         | like Heroku were created.
        
           | campbel wrote:
           | > If you want secure config storage on Kubernetes, you end up
           | using Secrets, which ends up being key-value like env vars
           | anyway.
           | 
           | You can load the secret file directly into the app, no need
           | to load it as env vars or keep it strictly as key-value
           | pairs.
           | 
           | > If you want similar security with Git you need a layer of
           | encryption, which breaks diffs and requires additional
           | tooling. This all leads back to why high-level deployment
           | tools like Heroku were created.
           | 
           | You can use tools like ejson[1] or sops[2] to get encrypted
           | files checked into Git that have key level granularity on
           | diffs.
           | 
           | There is definitely a place for higher level abstractions
           | than Kubernetes. Mostly it gives operators a standard
           | platform to build from when teams outgrow the PaaS sandbox.
           | 
           | [1] https://github.com/Shopify/ejson [2]
           | https://github.com/getsops/sops
        
             | Spivak wrote:
             | > You can load the secret file directly into the app
             | 
             | Now you're getting away from the spirit of 12factor and
             | hard-coupling them again. The intent is for the app to
             | consume the secrets but have no knowledge or care where
             | they came from.
             | 
             | Edit: misread as "load the secrets directly into the app."
             | Yeah, this is just env vars but different.
        
               | yjftsjthsd-h wrote:
               | I'm not really kubernetes expert, but don't they come
               | from /my-secrets and the application doesn't need to care
               | how that got mounted?
        
               | derefr wrote:
               | My trick for this with k8s Secrets, when there's some
               | sort of config file that absolutely must be consumed from
               | disk, is to
               | 
               | 1. create a Secret that defines the file as a key
               | 
               | 2. mount that Secret into the container
               | (.secret.secretName in the volume definition) under an
               | arbitrary conventional path, e.g. /secrets
               | 
               | 3. define an env-var _that gives the full path to the
               | file_ ; where the fallback when this env-var _isn 't_
               | defined is to find the file in the working directory with
               | a conventional filename.
               | 
               | If your own code uses the file, it can read the path from
               | this env-var; while if the core of your container is some
               | opaque tool that doesn't know about the env-var but
               | _does_ take the file 's path on the command-line, then
               | you can wrap the tool in an entrypoint script that reads
               | the env-var and passes it as a command-line arg to the
               | tool.
               | 
               | IMHO, this approach is flexible but principle-of-least-
               | surprise obeying for both development and production
               | deployment.
        
               | inferiorhuman wrote:
               | That sounds an awful lot like a .env file to me.
        
               | pojzon wrote:
               | yup thats exactly what .env is, and tbh that doesn't
               | change anything in the bigger picture
        
               | kelnos wrote:
               | > _Now you 're getting away from the spirit of 12factor_
               | 
               | The entire point here is that 12-factor isn't the be-all,
               | end-all, so this is irrelevant.
               | 
               | > _The intent is for the app to consume the secrets but
               | have no knowledge or care where they came from._
               | 
               | This doesn't really make sense, and is an impossible
               | requirement. The app will always have to know where
               | configuration or secrets come from. Environment variables
               | is just one option of "knowing where they come from".
               | Choosing file storage is just as valid.
        
               | kagakuninja wrote:
               | I don't use kubernates. We have JVM servers running on
               | AWS. We load secrets from Secrets Manager. Configs are
               | mostly HOCON files stored in S3. The knowledge of where
               | to find them is configured via env vars.
               | 
               | We use a custom setup in which the config values are
               | loaded from multiple sources actually, so we could put
               | everything into secrets manager, or load them all from
               | env vars or HOCON files.
               | 
               | If we had to set every config value in ECS / lambda using
               | env vars, it would be a major pain in the ass, and error
               | prone.
        
               | pojzon wrote:
               | > We load secrets from Secrets Manager
               | 
               | And how your apps authenticate to Secrets Manager ? Did
               | you ever call `env` on a pod that has IRSA configured ?
               | 
               | This is just a middle step to do exactly the same thing
               | but instead of using envFromSecret you use envFromSM
        
               | maccard wrote:
               | Not OP but an IAM role with scoped access to secrets.
               | Better again, using secretsFrom in the task definition
               | which injects the secret as an environment variable for
               | you.
        
           | redeux wrote:
           | > somewhat influenced by how Heroku works
           | 
           | The Twelve-Factor App is marketing content produced by the
           | former Founder & CTO of Heroku. It's a great piece of content
           | no doubt, but it should be viewed with that lens.
        
             | probotect0r wrote:
             | If it's marketing content then it's not doing a very good
             | job. I have known about the 12factor app principles for a
             | long time, and only found out today that it has ties to
             | Heroku.
        
         | xwdv wrote:
         | If it is truly harmful, it shouldn't be sitting at the top of
         | Hackernews. Let's flag it.
        
           | zemo wrote:
           | I... don't think the "flag" functionality is intended to
           | communicate "this is bad advice".
        
             | xwdv wrote:
             | It is "harmful" advice, not bad. Regardless, why should it
             | sit on the front page of hackernews?
        
               | zemo wrote:
               | it's really hard for me to express just how much content
               | hits the front page of HN that, if you follow it, will
               | make you worse at programming.
        
           | v2223943777435 wrote:
           | i got a ton of value out of reading the comments though
        
         | wutwutwat wrote:
         | Heroku didn't invent ENV variables nor did it dream up the idea
         | of using them for app config. This has been in use long before
         | Heroku existed. I'll give Heroku credit for making it common
         | knowledge and spreading the use of these concepts, though.
        
           | zemo wrote:
           | yeah I never suggested that heroku invented environment
           | variables, and the idea that environment variables weren't
           | common knowledge before heroku came along is ... let's say at
           | best revisionist.
        
             | wutwutwat wrote:
             | True.
             | 
             | If you remember the world before 12 factor you know that
             | even if motivated by self serving incentives, Heroku making
             | this mainstream has been an overall win for the entire
             | industry and we are generally better and more secure for
             | them having done so. We are still benefitting from all of
             | this today, and Heroku who championed it is making death
             | rattles.
        
               | hirako2000 wrote:
               | While valid points in many contexts, I've seen many other
               | contexts where following this principle/pattern a/ yields
               | none of its benefits b/ results in very significant
               | business impact.
               | 
               | Here is some obvious case observed many times.
               | 
               | A small and very fast pace team builds a server
               | application having limited to no external dependencies.
               | No db even.
               | 
               | Just a server doing stuff.
               | 
               | It does have some config yes, many many key/value pairs
               | even, a config file ended up with a dozen properties just
               | to control Req per sec limits and such.
               | 
               | Now just to deploy the thing, since of course env defined
               | configurations is where things belong in best practice,
               | the dev team setup a process to peer review those env
               | values and despite all of that, fail to get the app
               | running.
               | 
               | Human errors happen but since more mistakes and release
               | delays were caused by misconfiguration than any other
               | reasons, the keen and bright enginnering team all agreed
               | it would be better to have a config repo. One genius in
               | the group argued that having a config service would be
               | even neater than have the app read from a git repo! The
               | whole got so hyped and of course spend the whole next
               | sprint building and testing that. Brilliant, it worked!
               | 
               | Except that of course outages happened, of course.
               | Outages happen.
               | 
               | Bringing down all environments one by one, hence blocking
               | pretty much everyone. Everything went down because hey,
               | since we made that effort which took for longer than we
               | thought to build a config service and have our app reads
               | from it, one of the dev minds thought it was a genius
               | idea to have the app reload the config every 5 mins at
               | runtime so that if a configuration change was made, no
               | need to even restart anything. Of course error handling
               | wasn't robust and well tested since the boss reminded the
               | team we've got to ship a product by the end of the
               | quarter and solving the universe can maybe wait 5 or 10
               | years after the IPO.
               | 
               | Team resorted to simply track the config in the repo.
               | Kept the runtime reload of the config and things worked
               | just fine.
               | 
               | Things can be done right and impacts could have been
               | avoided. The example may seem like a twisted case since
               | these were all configurations, not environment specific
               | values. But I've just seen so many occurences of env
               | variables being separated from the application repo
               | costing so much and distracting contributors for no
               | benefit that I have grown very cautious of it.
        
               | ElevenLathe wrote:
               | The intent of this part of 12 Factor comes from the Bad
               | Old Days when devs didn't run their own stuff in
               | production. There was a dev team who could push code and
               | cut releases, and an ops team who would then deploy the
               | releases, and who was paged when things blew up.
               | 
               | If you store the database hostname/credentials, as an
               | example, in the "code" (either literally in the app code,
               | or in a config.ini that lives in the repo and gets baked
               | into a release), then the ops people can't quickly
               | repoint the app at a hot-spare database during an
               | incident: they'll need to edit the repo, push it, cut a
               | new release, etc. If there is a long build step (as for a
               | C++ app, for example), then this isn't really tenable
               | during an outage. If this is in an environment variable,
               | their normal deployment scripts can make the change for
               | them.
               | 
               | I agree that this is probably not necessary anymore if
               | your team is all wearing both hats (dev and ops), and its
               | just as quick to make an edit to the repo and cut/deploy
               | a new release as it would have been for the Ops team to
               | run their deployment script in the old days.
        
         | phendrenad2 wrote:
         | History repeats itself. Netlify tried this with the MACH
         | Alliance.
        
         | donatj wrote:
         | The invention of dotenv files just makes me chuckle and seems
         | to completely miss the point of not putting them in a config
         | file.
        
           | bunderbunder wrote:
           | dotenv files are great as a piece of developer ergonomics.
           | And that's fine. I use them for that purpose all the time. I
           | would not willingly consent to using them to manage
           | environment variables in production. But I would also not
           | willingly consent to doing it the way that's best for
           | production during development.
           | 
           | I think that's fine. 12-factor is a set of guidelines about
           | what you do _in production_. There 's a whole universe of
           | things that are smart ideas for development and bad ideas for
           | production. Leaving debug symbols in your binaries, leaving
           | ports for connecting a debugger open on your containers, etc.
        
           | scooble wrote:
           | I've read a bunch of tutorials promoting env vars to avoid
           | secrets being accidentally checked into git. Step 1 is
           | usually to create a .env file and add it to your gitignore.
        
         | rbanffy wrote:
         | "Store config in the environment" does not necessarily mean
         | configs are in environment variables - it only means
         | configuration comes from the hosting environment, not the
         | application itself - you are not adding a "settings.json" file
         | to your machine before starting the app.
         | 
         | This way, the same source deployed in, i.e. your AKS cluster in
         | Azure EU north will take the configuration you set for that
         | cluster while the one you run out of an Docker Swarm of RPi
         | Zero's in an Ikea photo frame (I have one cluster like that,
         | called Gibson), will take the configuration from that cluster.
        
           | Kwpolska wrote:
           | The page https://12factor.net/config disagrees with you:
           | 
           | > The twelve-factor app stores config in environment
           | variables (often shortened to env vars or env).
        
             | rbanffy wrote:
             | I would assume that is the interpretation back in 2011.
             | While the mechanism changed, the principle still holds.
        
         | lucideer wrote:
         | > _or you have k8s configmaps [...] those things are all
         | broadly fine; they keep the configuration state separate from
         | the app deploy state_
         | 
         | Why are they fine? This seems to be an argument grounded in
         | "it's a newer norm therefore it's better" but I don't see the
         | justification. I've never used Heroku but ConfigMaps are a bane
         | to manage. Even as a general programming principle within
         | application code, colocality is a well-known, long-discussed
         | topic around improving maintainability. If you're not using
         | monorepos, that's currently not a reasonable option with
         | current orchestration conventions.
         | 
         | Even if you are using monorepos, you're highly likely to either
         | get into writing & automating DSLs to feed into your configmaps
         | or do weird things like using Jinja to do file includes in your
         | mounts. That's all before you run into the etcd 1MB size limit
         | & realise that actually this whole system is a hack.
         | 
         | TL;DR newer ain't intrinsically better
        
         | throwaway4good wrote:
         | As far as I can tell this is the standard way of doing things
         | also in a kubernetes setup: The application gets its
         | configuration from the environment variables.
         | 
         | Then in the case of a kubernetes setup. The application is
         | wrapped inside a mechanism that gets the configuration from
         | some where else.
        
         | ljm wrote:
         | Nobody calls it '12 factor' anymore but we still adhere to a
         | generic set of principles as a result (since 12-factor predated
         | docker and kubernetes in the mainstream).
         | 
         | - Logs as a stream: yep, just write your logs to STDOUT instead
         | of a file. Let the orchestrator deal with reading and storing
         | it.
         | 
         | - Configuration in the environment. Apps tend to pull config
         | from different sources depending on the deploy - maybe a .env
         | locally and a secret store in production.
         | 
         | - Port binding - your app listens on a port and you put nginx
         | or whatever in front of it and set up a reverse proxy. K8S
         | services and ingress do that.
         | 
         | I think the biggest criticism you can lay down on 12 Factor
         | these days is that it's not actually written very well and
         | assumes you know exactly what it is talking about.
        
           | talent_deprived wrote:
           | > Port binding - your app listens on a port and you put nginx
           | or whatever in front of it
           | 
           | Apache for my personal projects, in front of the docker
           | containers.
        
           | morelisp wrote:
           | It's written very well but the audience is server application
           | programmers in 2011, not someone who has written a dozen
           | services yet never seen a log rotator, "production mode", or
           | an AF_UNIX socket in their whole career.
        
           | rbanffy wrote:
           | > - Logs as a stream: yep, just write your logs to STDOUT
           | instead of a file. Let the orchestrator deal with reading and
           | storing it.
           | 
           | This is one that always feels odd to me: what happened to
           | rsyslogd?
        
       | zoogeny wrote:
       | Seems good as a general heuristic but bad as a requirements
       | checklist.
        
       | lhnz wrote:
       | It's pretty bad that, more than a decade after this was released,
       | there are detractors for some of the guidelines but yet nobody is
       | able to point to a singular more modern set of principles. A lot
       | of our advice is now nuanced and spread out all over the
       | internet...
        
       | qwerty4343 wrote:
       | It's good to see it coming up again. I hope there will be a
       | platform among the new PAAS players that is 100% compatible with
       | the 12-factor app principles.
        
       | PaulKeeble wrote:
       | I have always disagreed most with the config advice. Config ends
       | up getting defined by a lot of different parties and quite often
       | by developers and its often best to ship the application with
       | reasonable defaults and then allow overrides from environment
       | specific files and then environment variables. This is the most
       | flexible for most server side applications because you often know
       | what a setup should be and its best source controlled but secrets
       | need to be injected at run time. Configuration needs to have
       | cascading levels to make life easiest in dev, test and production
       | without enormous setup time.
        
         | Kinrany wrote:
         | You can, and probably should, have reasonable defaults.
         | 
         | Configuration should be version controlled, but separately from
         | source code because it describes deployments, not images used
         | for deployments.
        
         | willseth wrote:
         | Why not also express the defaults as config, or is that what
         | you meant? I don't think that runs afoul of 12 factor.
        
         | schneems wrote:
         | > Configuration needs to have cascading levels to make life
         | easiest in dev, test and production without enormous setup
         | time.
         | 
         | The backdrop of when the 12factor app was written is that you
         | can toggle modes like that with RAILS_ENV=test (an environment
         | variable).
         | 
         | One way I think of the config section is "does your config
         | strategy play well with containers?" Once you build an image
         | you have a static disk state that doesn't persist changes
         | unless you make a new image. If your config is file based
         | (only), then to toggle between test and production behavior you
         | would have to build a whole new image.
         | 
         | By allowing config to change without the underlying disk, it
         | helps to isolate changes. Did the app break because something
         | in the deploy (image generation) broke? Or is it a bad config?
         | If you decouple image generation from configuration changes it
         | eliminates that question.
        
           | ahtihn wrote:
           | > Once you build an image you have a static disk state that
           | doesn't persist changes unless you make a new image. If your
           | config is file based (only), then to toggle between test and
           | production behavior you would have to build a whole new
           | image.
           | 
           | You could mount a volume with the config file. The nice thing
           | is that this lets you change the config at runtime (assuming
           | the app watches for file changes). Env vars cannot be changed
           | at runtime.
        
         | CraigJPerry wrote:
         | So long as you are systemically protecting against someone
         | spinning up a QA instance against some Prod resource then it's
         | probably alright to ship most config with the app. You don't
         | want to allow a (entirely predictable) human error to cause
         | 100k auth denied retries against a prod job submission queue
         | from QA for example. Much config isn't about infra though, it's
         | often what kind of ResolverStrategy (made up but plausible
         | sounding name) bean do I want to wire in each env for example.
        
         | sethammons wrote:
         | > application with reasonable defaults
         | 
         | defaults for dev or defaults for prod :)
         | 
         | If you say dev, you will eventually break prod. And prod
         | defaults may not make _any_ sense for dev.
        
           | berkes wrote:
           | > And prod defaults may not make _any_ sense for dev
           | 
           | They are actually rather dangerous even. You certainly don't
           | want your dev (or test CI) to reset the database if you are
           | accidentally connected to prod. Same for email-endpoints,
           | CDN, a message-bus, etc.
           | 
           | I've had my share of near-heart-attacks where I send out 10k
           | emails or thousands of payments _on actual production
           | services_ because of a spagetti config loaders with stuff
           | that boils down to `if (getHostname() ===  "localhost") {
           | include("dev.json") } else { include("prod.json") }`.
           | 
           | I'd prefer a hundred .envs and whatnots to avoid just one of
           | these scares.
        
       | porsager wrote:
       | Would be nice to have a simple term for all these absolutist
       | ideas about how to solve a Problem. I nominate the term "The Holy
       | Grail Jail".
       | 
       | I've actually had great success doing the opposite to most of
       | these points.
        
       | dathinab wrote:
       | every time I read Twelve-Factor App I'm like who they set up
       | twelve factor authentication, that's crazy isn't 2FA enough
       | 
       | then I remember that it's about something else
        
       | theusus wrote:
       | I hate things like these. They just make people architects and
       | SEs create forceful indirection that creates nothing but burden.
        
         | bdcravens wrote:
         | Perhaps in 2023, but in 2011, we were still at the edge of when
         | people would deploy by moving folders and individual files via
         | FTP, hard-coding credentials, and dropping in extracted zip
         | files as a method of dependency management.
         | 
         | Of course even then added layers were often considered
         | "burden", but in general, we need some degree of order lest we
         | create an unmanageable mess of index.old.3 and files edited
         | directly on production.
        
       | adolph wrote:
       | _The 12 factor app guidelines are still relevant today since many
       | software teams are building distributed systems, which require
       | careful attention to detail as to how services are coupled,
       | configured, and deployed. The 12 factor app is a wonderful
       | starting point for best practices. In my experience though they
       | omit a few facets and leave room for antipatterns in certain
       | facets._
       | 
       | https://smallbatches.fm/5/transcript
       | 
       |  _[00:03:52] Joe Kuttner: But today the infrastructure that we
       | run on is disposable, right? It 's much more like livestock or
       | cattle where we can buy new ones when we need one at market and
       | we can dispose of, you know, something's not working. Right. We
       | get. Yeah. So it's a very, it's a very different model._
       | 
       |  _[00:04:07] Joe Kuttner: And that was just emerging when the 12
       | factor app was first launched. I think it did a great job of
       | helping bring people into that state of mind._
       | 
       | https://smallbatches.fm/20/transcript
        
       | nopurpose wrote:
       | I wish there was a generic control plane for generating and
       | shipping config to apps. Kinda what Envoy proxy has, but capable
       | of shipping arbitrary schema.
       | 
       | Many complications of config management will go away then. Every
       | app instances knows just its ID, then everything else (region,
       | cluster, stage, current experiment, maintenance, anything else)
       | is discovered by "configd" service and config suitable for that
       | very instance is returned. Not unlike feature flags, but for
       | backend services.
       | 
       | OPA (Open Policy Agent) comes close: it has data sources (so it
       | can fetch inventory from cloud providers, kubernetes, any other
       | internal system) and Rego language to program config generation,
       | but it doesn't have means to notify instances of config updates,
       | so they have to resort to polling.
        
       | wly_cdgr wrote:
       | "last updated 2017" if you look at the footer
        
       | RyeCombinator wrote:
       | Thought we are up to 15 now?
       | https://domenicoluciani.com/2021/10/30/15-factor-app.html
        
       | freedomben wrote:
       | Over the years I've had a lot of discussions around 12 factor
       | apps and encountered a lot of confusion. I find that the
       | 12-factor site is great, but mainly for people that already
       | understand _why_ those things are important.
       | 
       | For people that don't already understand the _why_ behind the
       | rules, it took more in-depth explanation. For that purpose, I
       | made the video:  "What are 12 Factor Apps and Why Should You
       | Care?"[1]. I know of a couple of companies that use that for new-
       | hire orientation for engineers/devops that have said it's been
       | really helpful.
       | 
       | Regardless where you get the information, it's worth taking an
       | hour or two to learn about 12-factor apps. Most of these "rules"
       | are just things that take awareness and aren't immediately
       | obvious unless you've learned them all the hard way by making the
       | mistakes and feeling the pain.
       | 
       | [1] https://youtu.be/REbM4BDeua0
        
         | commandlinefan wrote:
         | It's like unit testing. The idea of writing code to test your
         | code was so blindingly obvious to me from the time I started
         | programming that I was shocked that it took so long for other
         | people to pick up on it, but the concept of "unit testing"
         | started to gain some traction around the late 90's and I was
         | relieved that somebody with some authority was advocating it.
         | 
         | But the reason I didn't just do it myself before Kent Beck
         | published JUnit was because the code I worked on didn't lend
         | itself well to being controlled by other code. Global variables
         | everywhere, widely distributed state, dependence on external
         | systems and specific file system layouts and general disregard
         | for modularity made it impossible to run anything outside of
         | the context it was designed in. All of this was "bad design",
         | but it met deadlines, so everybody did it anyway. I was
         | _hoping_ that if unit testing gained some traction, programmers
         | would get away from monolithic design.
         | 
         | After 25 years of observing unit tests for getters and setters
         | and then one big unit test that creates an in-memory database
         | because every function in the app needs a live database just to
         | run and then gets commented out because it fails, I've lost any
         | faith that unit testing will ever be more than a meaningless
         | checkmark that everybody fills out because it's a "best
         | practice" but nobody actually stops to think why.
        
           | berkes wrote:
           | I'd like to believe that "everybody" and "nobody" in your
           | story aren't as black-and-white.
           | 
           | Many people see the value of unit testing. And see how TDD
           | actually shapes your code to be testable and (accidentally,
           | well, not really) therefore also cleaner and better
           | organized.
           | 
           | I've always said, and will continue to do so: good,
           | maintainable code, is accidentally also very well testable
           | code. And vice-versa. And it's not a real accident, if you
           | think about it.
        
           | freedomben wrote:
           | I've seen similar as well. Aggressive scrum has also made
           | this much worse by laying all the incentives at "ship now and
           | let the poor sap later who has to work this figure it out. I
           | got my 'story points' and shipped so I won't be under the
           | metrics microscope." Having a general requirement of "each
           | code change should have tests in order to pass code review"
           | can definitely help, but it doesn't incentivize _good_ tests,
           | and IMHO the only thing worse than no tests are bad tests.
        
       | throwaway4good wrote:
       | Holds up well 12 years later.
        
       | lakomen wrote:
       | Not this again. Let the horse die already
        
         | commandlinefan wrote:
         | This horse or any other horse. I read this and I think "this is
         | good advice, we should follow it." You read it and you think
         | "this is good advice, we should follow it." And then we both
         | do, but our interpretations conflict completely so we end up
         | arguing about the true definition of "implicit existence of
         | system-wide packages".
        
       | robbywashere_ wrote:
       | Fun to compare this to with how insane deployments are now
       | featuring kubernetes manifests which only big brains can
       | understand
        
         | mplewis wrote:
         | What part of the Kubernetes manifest do you think makes
         | deployments insane?
        
           | xdennis wrote:
           | Not GP, but here's an example I've seen. Kubernetes files are
           | generated by Jsonnet. It is invoked from makefiles. Makefiles
           | are spread across different git modules. The scripts require
           | various Go binaries. Most are installed through a command,
           | some have to be installed manually. If the version is wrong,
           | mysterious errors happen.
           | 
           | I once had a problem with the generated files so I deleted
           | the generated directory and regenerated everything. Turns out
           | you can't do that. The generated files are edited, and if you
           | generate from scratch you've wiped out the manual edits.
           | 
           | Note that I'm not criticizing Kubernetes here. But when you
           | have a non-simple system with some missing aspects, some
           | people take the opportunity to make it even more complex.
        
       | pizzaknife wrote:
       | makefile cackles as it observes the exchange
        
       | dang wrote:
       | Related:
       | 
       |  _Ask HN: Is 12factor.net Still Relevant?_ -
       | https://news.ycombinator.com/item?id=36283702 - June 2023 (6
       | comments)
       | 
       |  _12 Factor App Revisited_ -
       | https://news.ycombinator.com/item?id=33164407 - Oct 2022 (7
       | comments)
       | 
       |  _Twelve-factor app anno 2022_ -
       | https://news.ycombinator.com/item?id=31225921 - May 2022 (35
       | comments)
       | 
       |  _The Twelve-Factor App (2011)_ -
       | https://news.ycombinator.com/item?id=31198956 - April 2022 (102
       | comments)
       | 
       |  _Twelve-factor app development on Google Cloud_ -
       | https://news.ycombinator.com/item?id=21415488 - Nov 2019 (63
       | comments)
       | 
       |  _The Twelve-Factor App_ -
       | https://news.ycombinator.com/item?id=19947507 - May 2019 (3
       | comments)
       | 
       |  _12 Factor CLI Apps_ -
       | https://news.ycombinator.com/item?id=18172689 - Oct 2018 (247
       | comments)
       | 
       |  _12 factor app configuration vs. leaking environment variables
       | (2014)_ - https://news.ycombinator.com/item?id=15869436 - Dec
       | 2017 (2 comments)
       | 
       |  _Ask HN: Alternative to Heroku that doesn 't enforce 12-factor_
       | - https://news.ycombinator.com/item?id=10628961 - Nov 2015 (1
       | comment)
       | 
       |  _The Twelve-Factor App_ -
       | https://news.ycombinator.com/item?id=10288216 - Sept 2015 (3
       | comments)
       | 
       |  _The Twelve-Factor App_ -
       | https://news.ycombinator.com/item?id=9492120 - May 2015 (2
       | comments)
       | 
       |  _Twelve-Factor Applications with Consul_ -
       | https://news.ycombinator.com/item?id=7780249 - May 2014 (2
       | comments)
       | 
       |  _The Twelve-Factor App_ -
       | https://news.ycombinator.com/item?id=7547687 - April 2014 (1
       | comment)
       | 
       |  _Building Twelve Factor Apps on Heroku_ -
       | https://news.ycombinator.com/item?id=6219444 - Aug 2013 (1
       | comment)
       | 
       |  _12 Factor model for architecting SaaS applications_ -
       | https://news.ycombinator.com/item?id=6060381 - July 2013 (1
       | comment)
       | 
       |  _The Twelve-Factor App_ -
       | https://news.ycombinator.com/item?id=5979452 - July 2013 (1
       | comment)
       | 
       |  _12factor: Methodology for Building Software-as-a-Service Apps_
       | - https://news.ycombinator.com/item?id=4027026 - May 2012 (1
       | comment)
       | 
       |  _Twelve Factors of Web Application Development_ -
       | https://news.ycombinator.com/item?id=3267187 - Nov 2011 (37
       | comments)
        
       ___________________________________________________________________
       (page generated 2023-10-12 21:00 UTC)