[HN Gopher] Imperative configs are out; Declarative configs are in
___________________________________________________________________
Imperative configs are out; Declarative configs are in
Author : ayf
Score : 97 points
Date : 2022-11-09 18:26 UTC (4 hours ago)
(HTM) web link (www.prodvana.io)
(TXT) w3m dump (www.prodvana.io)
| zmgsabst wrote:
| This example is backwards:
|
| Showing a map printout while ignoring runtime state is
| declarative; executing directions step by step, adapted to the
| present state of the system, is imperative.
|
| I'll set aside my feelings about stretching meaning to say -- if
| you are going to use technical words this way, don't use them
| _backwards_.
| shkkmo wrote:
| Declarative works better than imperative only when someone has
| already defined the imperative process that takes you from any
| given state to your goal and does that for you based on your
| declaration.
|
| Google maps works great for a set of origins to a set of
| departures for certain modes of travel. Once you get outside
| those bounds, it can fail in significant ways.
|
| Thus the entire premise of the article thus seems to boil down
| to: If someone has already done the work, don't re-do it.
|
| The problem with declarative configuration is that as the systems
| they manage become more complex, inevitably you leave the bounds
| of the solved problem and have to start solving it for yourself
| imperatively.
| Jtsummers wrote:
| > Declarative works better than imperative only when _someone
| has already defined the imperative process_ that takes you from
| any given state to your goal and does that for you based on
| your declaration. [emphasis added]
|
| No, someone (as in a person) doesn't have to define the
| imperative process, that would be stupid. We have computers to
| compute things, including computing new processes. Oracle
| didn't hire a bunch of human query planners to manually
| construct every query plan, they made a query planner. Google
| didn't sit down a nation of navigators to plan out every route,
| they wrote a program (or likely a set of programs) that
| computes it on the fly.
| shkkmo wrote:
| > they wrote a program (or likely a set of programs) that
| computes it on the fly.
|
| Which is precisely the sort of imperative process I am
| referrencing...
| righttoolforjob wrote:
| This is the correct answer.
|
| You can declare only when someone else already has performed
| the imperative chore, i.e. written the source code, programmed.
|
| Q.E.D.
| [deleted]
| otikik wrote:
| Hey author: your css seems off on mobile. Text reads fine, but
| code looks tiny in comparison
| ayf wrote:
| A good overview of why legacy CD systems cause so much pain in
| cloud-native environments
| [deleted]
| 0xbadcafebee wrote:
| _" My dad switched from working with a map printout without
| knowing real-time conditions, the imperative flow, to Google Maps
| with turn-by-turn directions, the declarative flow."_
|
| There is no such thing as an imperative or declarative "flow". By
| its very definition, declarative does not have "flow". It is just
| a statement. Imperative (in programming) literally means
| "describing steps that change state". Declarative (in
| programming) literally means "describing a state which is
| desired".
|
| Declarative would be "I want a cheeseburger." Imperative would be
| "Get me a bun, and some lettuce, and tomato, and mayo, and raw
| meat. Cook the meat on a grill at high heat, flipping half way.
| Put the mayo on the bottom of the bun, then the meat, then the
| lettuce, then the tomato, then put on the top of the bun. Give it
| to me."
|
| It's still strange to me how people learn about "magic words"
| like declarative and imperative, and then try to
| ssstttrrreeeeettttttcccccccchhhhhhhhhhh their meaning into some
| new paradigm that they have just thought up.
|
| There is no such thing as an imperative configuration file. A
| configuration file describes how a program should be configured.
| Even when the configuration file "describes a series of steps to
| change state", it's _still declarative_ , because the
| _configuration file_ is still declaring _to the program_ how to
| operate. It 's _the program_ that is imperative or declarative,
| depending on how it _interprets and acts on_ the configuration
| file. (This is made clearer in programs like Ansible, which,
| within the exact same configuration file, supports both
| declarative and imperative statements)
|
| Before you say "what about template files! jinja2! go templates!
| hcl!", that is merely a DSL, which is no longer a configuration
| file; it is effectively a program in a crude programming
| language, interpreted by an interpreter (the program loading the
| file).
|
| _(edit: I agree with the author 's point! But I suggest we stop
| using these terms 'declarative' and 'imperative', and instead say
| "let's write programs that are functional enough that we don't
| need to write configuration files that are mini-programs")_
| rising-sky wrote:
| I had the same thought when I read that paragraph and anecdotal
| tortured "lost with map printout" description, immediately
| thought, not sure this author knows quite what they are talking
| about, but kept reading to see what the configuration would
| look like...
|
| > ssstttrrreeeeettttttcccccccchhhhhhhhhhh
|
| apt
| sly010 wrote:
| Indeed. Reading SICP should be a requirement for starting a
| YAML company.
| phailhaus wrote:
| > Even when the configuration file "describes a series of steps
| to change state", it's still declarative, because the
| configuration file is still declaring to the program how to
| operate
|
| Not sure I buy that logic. With that reasoning, all code is
| declarative because it's "declaring to the
| interpreter/compiler" how to operate.
|
| I think he's making the same point you did. In one case, the
| config file is laying out a sequence of steps like you did for
| how to make a burger (imperative). In the second case, the file
| just defines the end state (declarative).
|
| Sure, you could say that the first case is declarative because
| the "end state" being defined is pipeline itself. But the point
| is that we're talking about how to get a burger, not define how
| to make one.
| lrem wrote:
| This is an important concept in my work. From experience,
| there is a pretty simple litmus test. It is pretty well
| understood that the word "imperative" denotes a system where
| the user describes a series of steps. The system executes the
| steps, in the end result bringing its universe to a desired
| state. The user does not need to describe what that is to the
| system, but a proof of correctness would decide whether that
| has been reached. Now, a declarative system is one where the
| user describes the desired end state and invariants on the
| admissible intermediate states of the universe. The system
| needs to figure out the current state of the universe, find
| the difference and find an admissible path to eliminate the
| difference. The litmus test to discern the two types of
| systems is whether the system concerns itself with the
| difference as a primary concept.
|
| To give an example familiar to this forum: imagine we're
| building a package. This can be set up imperatively with a
| shell script, or declaratively with GNU Make. Make will
| figure out which output files are missing or older than their
| input files, constituting a difference. It will figure out
| (topological sorting) an admissible path through computing a
| new up to date file from input files that are already up to
| date (re their own input files). Now that's cool and all. But
| the same end result can be achieved way simpler if you can
| skip the diffing bit. If you assume you're doing a clean
| build, you can order your build steps optimally in a shell
| script, skipping the now useless complexity. If there has
| been an abandoned rub that had some successful steps, your
| shell script will waste their results, but still end in a
| correct state.
| naphatkrit wrote:
| yep!
| atomicnumber3 wrote:
| I find discussions like this ("html is/isn't a programming
| language" etc) kind of tiresome because eventually you end up
| at "a compiler is a file format converter". You have reached
| Truth but it ends up not being a terrifically interesting
| statement and everyone just kind of goes back to whatever
| they originally meant with whatever they said that started
| the argument.
| throwawaaarrgh wrote:
| The more accurate people's words are, the clearer they
| communicate their ideas. Better communication leads to
| better results.
|
| Most people don't know that YAML is not a configuration
| format, or even the difference between a configuration
| format and a data format. Once you know the difference,
| it's clear they are _very_ different things, and when you
| should use which.
| 0xbadcafebee wrote:
| > With that reasoning, all code is declarative because it's
| "declaring to the interpreter/compiler" how to operate.
|
| Sort of! All code is also _imperative_ , eventually, at the
| machine code level. This is a perfect example of how useless
| the whole "imperative vs declarative" distinction is. Nearly
| everything in a computer is _both_ imperative and
| declarative, in some fashion, at some point.
|
| These terms were not made to be some concrete and inviolable
| paradigm of computing. Some academics just wanted to tell
| people to write programs where you didn't have to spell out
| every instruction to the compiler, so they made this crude
| distinction. Things like a function called
| _give_me_a_temporary_file()_ , rather than writing out an
| entire function yourself to create and return a temporary
| file. But both are executing steps imperatively _under the
| hood_. So we shouldn 't make a big deal about these terms or
| programs that do more of one or the other.
|
| The differences that I'm pointing out are 1) declarative does
| not describe a flow, the flow is under the hood; and 2)
| configuration files do not actually perform steps, they
| merely describe arbitrary data, and only the program
| interpreting the configuration can determine if the result is
| imperative or declarative. For some programs, the exact same
| configuration file may result in either imperative or
| declarative execution.
| jerf wrote:
| "All code is also _imperative_ , eventually, at the machine
| code level."
|
| This is essentially what I think, and I've thought for a
| long time: https://news.ycombinator.com/item?id=3507281
|
| To the extent that people say "But what about..." my answer
| is that there isn't a particularly useful line to draw
| between imperative and declarative. There is one; I can
| draw it too. I challenge its _usefulness_. Imperative
| things have too many declarative things mixed in, and vice
| versa, in practice for it to be a very useful metric. I
| find what I mentioned in that post about the ease of
| debugging to be the _real_ information I get when someone
| uses the "declarative" phrase; I can pretty much count on
| things breaking and me being unable to fix things whenever
| I see that word used.
|
| I find it much more useful to mix things up as appropriate
| and not sweat which things they happen to be. A
| "declarative style" is a useful tool to be used, little
| more, and it almost never belongs in any sort of pro or con
| list. The pros or the cons should be at the next level
| down, like, "it's hard to debug" or "I'm typing way too
| much for what I'm trying to accomplish". I haven't
| evaluated any techs and given or subtracted points merely
| for being or not being "declarative" in a long time.
| heavyset_go wrote:
| Python's setuptools configuration file via setup.py can
| perform steps, it's literally just a Python script.
| phailhaus wrote:
| > configuration files do not actually perform steps
|
| And I don't think he's saying they do either. In fact, I
| don't think the post gets into execution details at all! If
| you go through it again, he's only talking about ways of
| writing config files, not ways of running them. In one
| approach, the config defines the pipeline itself. In the
| second approach, the config defines your _desired end
| state_.
|
| The "imperative" vs "declarative" distinction is entirely
| dependent on what your goal is. If your _goal_ is to write
| a very specific pipeline, then the former is also
| declarative! But the context of the article is in achieving
| some desired end state in CI. With that context in mind,
| the former is "imperative" and the latter is
| "declarative".
| 0xbadcafebee wrote:
| Well, he does get into execution detais; he's showing you
| multiple configs that have a lot of steps in series, and
| saying, "Look, this file is ridiculous! Too many steps!
| Bugs! Instead, just define one function! Let the program
| deal with it!" Which I 1000% agree with.
|
| I get that the whole story is saying "do things
| declaratively". But I think that term, and its ability to
| be misused (as in the quoted example) are distracting,
| because we get lost in the weeds and miss the real point,
| which is that we shouldn't be writing pseudo-programs in
| configuration files. I think we can all agree on that; so
| let's just say that, and leave the magic words alone.
| phailhaus wrote:
| Where's the execution detail? He never tells you how the
| config file is going to be run. Again, the entire post is
| relative to the goal of getting a stable CI build. If
| your config file is filled with implementation details of
| that goal, you're being imperative.
|
| It has nothing to do with "writing programs in your
| config file". The problem is that your config file is
| telling the runner how to reach the goal rather than what
| the goal is.
| TOGoS wrote:
| Right. I think the important distinction is whether an
| imperative language is used to describe things that _could_
| be done purely declaratively, as is the case with Gradle[1].
| I think this happens all too often because the oldschool
| declarative system has some edge case that it can 't handle,
| so someone reinvents it with a fake DSL which is actually a
| dialect of an imperative language, and now you have the worst
| of both worlds. I've always thought that what we needed was a
| way to allow both declarative and functional information to
| be provided, but with a clearer separation between the two.
| e.g. a build configuration language based on this approach
| might allow one to specify either 'buildArtifactId: "xyz123"'
| or 'buildArtifactIdExpression: (functional expression to
| compute a build artifact ID goes here)'.
|
| I sort of tried to do this with the Factorio terrain
| generation system[2]. The first phase is to run a Lua
| program, which is imperative, but the result is an immutable
| object representing the terrain generation configuration,
| which in turn includes functional expressions, since a map
| where everything is constant would be boring.
|
| [1] I f&!@#^ing hate Gradle; it is my go-to example of
| thoughtlessly mashing paradigms together because you can,
| resulting in something that nobody I have ever met really has
| really been able to work with except by trial and error.
|
| [2] See https://factorio.com/blog/post/fff-200 and
| https://togos.github.io/togos-example-noise-programs/
| geuis wrote:
| Very good breakdown. Couldn't agree more.
|
| Except that mayo should go on top.
| tzs wrote:
| Alton Brown recommends mayo on the bottom to create a barrier
| to keep the bun from getting soggy from burger juices.
|
| But forget about the mayo...regardless of where one stands on
| proper mayo position there is a much bigger issue with the
| imperative cheeseburger instructions given: there was no
| cheese!
| 0xbadcafebee wrote:
| I was waiting for someone to catch that bug xD
| 0xbadcafebee wrote:
| I don't disagree, but a lot of burgers end up dry, so the
| bottom bun stays dry and bland, which makes me sad. Sauce on
| the bottom solves that, and the top bun gets wet from the
| tomato. Optimizing for the 80% burger.
| jdminhbg wrote:
| The oil in the mayo acts as an aquaphobic barrier between
| the tomato and the bun. You don't want a "dry" bun but a
| soggy bun is structurally deficient on top of being bad
| tasting.
| eurasiantiger wrote:
| "There is no such thing as an imperative configuration file."
|
| My babel.config.js would like to disagree.
| taeric wrote:
| > There is no such thing as an imperative configuration file
|
| My Emacs config would like a word with you. :D
| thfuran wrote:
| >There is no such thing as an imperative configuration file.
|
| Clearly you haven't seen the gradle files I have been subjected
| to.
| WastingMyTime89 wrote:
| You are missing the forest for the trees by confusing the end
| result with its implementation.
|
| The idea between having a declarative configuration vs a
| traditional one is well understood in the context of a machine.
| In one case, you describe how you want the final machine to
| look like and let the system decides how to reach that states,
| in the other you yourself input how the state will be changed
| step by step to reach the final stage. Sure the declarative
| configuration really is a DSL and there is an interpreter
| somewhere turning it into a step by step list of actions but
| that's invisible to the end user.
|
| It's not magic just abstracted. That's the good old Wheeler
| aphorism: " There is no problem in computer science that can't
| be solved using another level of indirection."
| ClumsyPilot wrote:
| > It's still strange to me how people learn about "magic words"
| like declarative and imperative, and then try to
| ssstttrrreeeeettttttcccccccchhhhhhhhhhh their meaning into some
| new paradigm that they have just thought up.
|
| Develipera make fun of how project managers call anything Agile
| without understanding what it means, then go and commit the
| same sin themselves.
| rgovostes wrote:
| As a university student discovering constraint solvers like Z3, I
| believed that the future of computing was declarative
| programming, with black box solvers that would obviate the need
| for specialized algorithms.
|
| Dealing with Webpack cured me of the idea that declarative is
| strictly superior. Configuring it is just endlessly consulting
| the documentation, and then the underlying code, and then cargo
| culting StackOverflow answers to coerce the black box to create
| the output I want.
| btown wrote:
| Declarative is great if there is always one path to get to where
| you're going.
|
| But I like to use the example of a Git repository where you've
| renamed and also changed some parts of a file. You commit the new
| state - was it a deletion and a brand new file, or a migration
| and modification of an existing resource? It's usually a non-
| issue because Git is smart, and files don't really care about
| their identity - but what if this was a database server and you
| need control over what happens when, and how things drain? Do you
| trust the system to choose the right path vs. "delete the
| database and make a new one?"
|
| At some point you'll need imperative thinking to make sure your
| bases are covered, even if that is "we have a 3 phase rollout
| plan for different versions of the declarative config." And
| that's perfectly fine, in many cases. But it's important to never
| get into a cargo-cult level of "if it can't be done with one
| declarative change it shouldn't be done" - because then you'll
| either get stuck or light things on fire.
| pmarreck wrote:
| As a recent NixOS convert, I concur
| kazinator wrote:
| > _If a pipeline fails halfway, most CD systems don't have the
| ability to retry. The only option is to restart a pipeline from
| the very beginning. Now with declarative configs, an intelligent
| delivery system, the system will detect that staging is already
| on the desired version and skip that push, allowing you to
| deliver value to your customers faster._
|
| Hey, an interrupted "make" can always restarted from where it
| left off without ever having to do a "make clean", so what could
| go wrong.
| andybak wrote:
| This is an old debate surely? My first awareness of it was
| probably related to Django's settings.py back around 2007 but I
| got the feeling from discussions then that it was a discussion
| that had been bouncing around for a long time before that.
|
| The counter-argument is that declarative configs inevevitably
| sprout programming-like features - and if they don't someone will
| write code to generate them.
|
| (disclaimer - in true HN fashion I haven't properly RTFA'd -
| dinner is nearly ready and I'm feeling bold)
| gtirloni wrote:
| The whole article is an ad.
| nickcw wrote:
| > The counter-argument is that declarative configs inevevitably
| sprout programming-like features - and if they don't someone
| will write code to generate them.
|
| Ha! See the make manual for examples.
|
| Background reading:
| https://stackoverflow.com/questions/3480950/are-makefiles-tu...
| naphatkrit wrote:
| Yep the idea of declarative winning out over imperative is
| nothing new. Yet for some reason, most deployment systems make
| you go through an imperative flow if you want to orchestrate
| releases.
| taeric wrote:
| Because "burn it all down and rebuild everything" is by far
| the easiest orchestration to create from a declarative
| config. And is almost certainly not what any technician is
| going to want to happen to their production system.
| scubbo wrote:
| Sort-of a nitpick, but for stateless services that's
| _precisely_ what you want to happen!
| taeric wrote:
| Not when your infrastructure includes your data store,
| though. Or if it includes such things as your application
| endpoint. You may want to stand up the new endpoints and
| then drain over traffic, as an example. Not take an
| outage for a deployment.
| bastardoperator wrote:
| You might end up with the nightmare config they highlighted if
| the person writing the config has no semblance of DRY, but it's
| unlikely someone versed in CD systems isn't going to use
| techniques afforded to them for reducing boilerplate.
| taeric wrote:
| I.... don't buy the initial example. Google maps is still an
| imperative list of directions. It just keeps track of the current
| pointer for you, and is able to reroute. If anything, that is an
| example that a dynamic action plan is more adaptable than a
| static one.
|
| That said, declarative clearly has advantages when it can work.
| You probably still want a "break glass" way to see what the
| actual steps that will be taken are, but can get surprisingly far
| without that.
| naphatkrit wrote:
| Oh totally that declarative can and will often compile down to
| imperative. The question is what do you ask the user for? My
| take is Google maps is declarative in the sense that you ask
| for your destination, your constraints (e.g. no highway), and
| time to leave, and it dynamically generates the underlying
| imperative steps like you said (but continuously adapt if you
| go off course, which you cannot do unless the initial input was
| declarative).
| taeric wrote:
| That example still feels off. The "offline printout" was done
| in a very declarative way. It is just fixed on the execution
| plan. (And, quite frankly, less obnoxious to me if I decide
| to stop and get gas or food.)
|
| You can even prepare alternative routes ahead of time, if you
| want to speculate on conditions. Is a good idea to role play
| some of that ahead of the time, in any case.
| naphatkrit wrote:
| That makes sense. I'm not necessarily arguing for
| continuous intelligence in this post, just that declarative
| configs enable it. Even the initial conversion of a
| destination to a set of directions is a compilation of
| declarative to imperative. Imagine if Google Maps was truly
| imperative and asks you to input the individual roads you
| want ahead of time - then it would not be useful.
| taeric wrote:
| Apologies if it sounded like I didn't like the post. I
| think the rest of it was great and your point, I think,
| is conveyed well. I just got hung up on the lead in.
|
| Getting any directions from a system will be hard to turn
| into an imperative process, to be honest. It is
| imperatively telling you what to do, already. And,
| amusingly, you typically use an imperative to activate
| it. "Google, show me directions from here to ____."
| Literally an imperative voice cue. (I think you can twist
| this to be more "I want directions from here to there,"
| such that it is not imperative, but this feels like it is
| stretching.)
|
| Declarative would be a bit more itinerary based. You fill
| in a few details of "On this day/time, I want bagels. On
| this day/time, I want to be at a hotel in the city. Etc."
| Then, it would output a list of steps on how you could
| make that work.
|
| And this ties it in nicely with infrastructure config.
| Especially at the beginning when you don't have pre-
| existing state of services, you can quite easily declare
| what you want. It is more when you have to also start
| defining migrations that you are likely to drop into
| imperative steps. (Really, any management of state
| transition will almost certainly be easier using
| imperative.)
| shkkmo wrote:
| > Imagine if Google Maps was truly imperative and asks
| you to input the individual roads you want ahead of time
| - then it would not be useful.
|
| I'd love to be able to have the ability to request
| specific roads in Google Maps directions, as it is you
| have to jury-rig imperative elements by adding waypoints
| at the start and end of the route you want included.
|
| This illustrates me point: Systems for transforming
| declarations into imperative steps will always have
| limitations in terms of what can be declared and what
| states that can generate good imperative steps from/to.
| Once you step outside those limitations, you inevitably
| need to re-introduce some amount of your own imperative
| configuration. Ideally, eventually you make that
| imperative logic declaratively configurable and
| contribute that back to the commmunity.
| everforward wrote:
| Declarative stuff gets converted to imperative at execution
| time. A significant difference is that a declarative system
| is not dependent on a particular state.
|
| Telling a site to get you from X to Y is declarative,
| because X can be anything and it works. The printed out
| instructions are imperative, and only work for a fixed X or
| somewhere already on the path.
|
| Maps is more declarative because it continually generates
| imperative instructions to get to Y regardless of where you
| currently are.
| taeric wrote:
| Right, but the point is that when you are in the
| execution, you are back in the imperative. And with
| google maps, you are almost always in the execution side.
| Is why I said that would be a better example for dynamic
| plans being better than static ones.
|
| I'd go further and say that not just dynamic and static,
| but interactive versus non-interactive is the showcase
| there. Declarative/imperative is just not that applicable
| to that scenario.
|
| And again, I do like the rest of the article and the idea
| that is getting explored. I just question if that is
| really a great example for declarative/imperative. You
| have to squint to make it work, for whichever version you
| want to support.
| [deleted]
| sly010 wrote:
| This just looks like a frontend over kubernetes.
| naphatkrit wrote:
| Not exactly? Kubernetes natively doesn't have a solution for
| pushing across environments afaik.
| moralestapia wrote:
| >My dad switched from working with a map printout without knowing
| real-time conditions, the imperative flow, to Google Maps with
| turn-by-turn directions, the declarative flow.
|
| LOL, he got it backwards, "turn-by-turn directions" is the
| imperative flow. Can't expect much after reading that.
| phailhaus wrote:
| I was thinking the same thing, but his point is that with
| Google Maps you simply declare your end goal. It figures out
| how you should get there based on current conditions.
| naphatkrit wrote:
| I guess I should have said "voice-guided" turn-by-turn huh? h
| ttps://www.forbes.com/sites/anthonykosner/2012/12/13/google..
| . is what i was referring to :)
| moralestapia wrote:
| Huh? Voice or not, it's still imperative. :grimacing:
| phailhaus wrote:
| "Declarative" and "imperative" are relative to the goal
| you are trying to achieve. If your goal is only to reach
| your destination within some parameters, then writing
| down every single step to get there is imperative. The
| declarative approach is to specify the destination and
| those parameters, and allow the engine to determine how
| to get there.
| saltcured wrote:
| Right, imperative directions would be the turn by turn
| recipe to get from A to B. Declarative directions would
| be "be at B". Route planning is the solution method to
| convert from declarative to imperative navigation
| directions.
|
| Of course, it's also a nuanced value judgement. The
| declarative/imperative abstraction is almost fractal, as
| we could consider a trip with multiple intermediate
| destinations as either a declarative itinerary or an
| imperative plan. Or we could go further and talk about
| all the control steps it takes to "turn right in 100
| meters".
|
| What the article describes for the father's driving is
| the difference between ahead-of-time and just-in-time
| navigation. It's not a very good metaphor for declarative
| versus imperative configuration management. To turn it
| into one would make for a perverse story, i.e. telling
| the father where you want to meet versus telling the
| father a long list of turns starting from the airport
| while obfuscating the destination.
| [deleted]
___________________________________________________________________
(page generated 2022-11-09 23:00 UTC)