[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)