5 ms·
Isolating complexity is the essence of successful abstractions
- picografix 2y agocomplexity has to live somewhere, code anxiety was a real thing for me
- amelius 2y agountil what happened?
- gsf_emergency 2y ago>The question is first of all whether we have written them down anywhere The only hard thing in software: papers please (easily accessible documentation)
- Vampiero 2y agoGive it a few years and it will be self-maintaining
- stuartaxelowen 2y agoThe hard part about documentation is that it requires you to have a component that can be comprehensibly and sufficiently documented. So much of the software written is seen as provisional, that even its authors think “well, we’ll document the v1”, not realizing that their prototype is just that.
- drewcoo 2y ago[flagged]
- bb88 2y agoPython showed what relaxed types could do. And we could go a long way as it turns out without types. But there are use cases for types, and even python admitted such when they added type annotations. However, when I was a kid a would put a firecracker next to an object. I didn't bother running the scenario through a compiler to see if the object was of type Explodable() and had an explode() method that would be called.
- saghm 2y ago> However, when I was a kid a would put a firecracker next to an object. I didn't bother running the scenario through a compiler to see if the object was of type Explodable() and had an explode() method that would be called. Duck typing: if it quacks like a duck, and it explodes objects next to it, it's a firequacker
- cjfd 2y agoDuck typing. If it quacks like a duck and swims like a duck it might be a duck. But it might also be a nuclear submarine doing a duck impersonation. The question is whether you want a nuclear submarine in your pond.
- marcosdumay 2y agoThe philosophy of duck typing is very clear in that yes, you should accept a nuclear submarine in your pound. The problems are that you won't remember to do the same exact checks everywhere and document them.
- stuartaxelowen 2y agoPython showed that you can be wrong about your types and still build a successful product.
- yakshaving_jgt 2y agoUnless your product is a Boeing 737 MAX.
- dartos 2y agoI don’t think I agree that either typescript nor rust successfully hide the complexity in their type systems. By the nature of type systems, they are tightly coupled with the code written around them. Rust has rich features to handle this coupling (traits and derives), but typescript does not.
- atoav 2y agoType systems like in Rust may introduce their own complexities, but they also help you tackle the complexity of bigger programs if wielded correctly. Typesystems can be complex to use, but in the end they constrain the degrees of freedom exposed by any given piece of code. With a type systems only very specific things can happen with any part of your code, most of which the programmer may have had in mind — without a type system the number of ways any piece of code could act within the program is way larger. Reducing the possible states of your program in the case of programming error is a reduction of complexity. Now I don't say type systems may introduce their own complexity, but in the case of Rust the complexity exposed is what systems programmers should handle. E.g. using different String types to signify to the programmer that your OS will not allow all possible strings as file names is the appropriate amount of complexity. Knowing how your program handles these is again reducing complexity. Imagine you wrote a module in a language where you don't handle these. Every now and then the module crashes specifically because it came across a malformed filename. Or phrased differently: The program does more than you intended, namely crashing when it encounters certain filenames. Good luck figuring that out and preventing it from happening again. With a type system the choice had to be explicitly made during programming already. Less things you code can do, less complexity. Many developers confuse complexity of the internal workings of a program with the complexity of the program exposed at the interface. These are separate properties that could become linked, but shouldn't.
- knome 2y agoI didn't get the general idea that the author thought they hid the complexity, but rather that they exposed and codified it. They gave the complexity that would previously live in your head somewhere it could be expressed. And once expressed, it can be iterated on.
- PittleyDunkin 2y ago"Parameterizing complexity" is probably a better way to say it. There's no isolation when it comes to software.
- hackit2 2y agoI don't think it is anything to do with complexity, or grouping code/data, its just a natural tendency of people to categorize things together that display a high degree of class inclusion. And some categories are easier to deal with than others.
- atoav 2y agoNot sure if I agree Let's say you have a poem program, that reads files from your drive and turns them into poems. A well isolated/abstracted variant of that program is as simple as a blackbox with two or three inputs and a single output. One of the inputs are the files, the others might be a configuration file or user adjustable parameters like length. The program is well isolated if you can't give it any combination of inputs that doesn't produce a poem or an error message related to the usage of the program. A badly isolated variant of the same program would be one where the user had to think a lot about the internal behavior of the program, e.g. how file names are handled or where so many parameters of the poem generation have to be supplied as parameters, that the user essentially has to rewrite the core of program with their parameters. Or the user could supply a file that allows them to gain RCE or crash the program.
- quotemstr 2y agoI have always felt that it's better to "concentrate" complexity into one key component and make the rest of the codebase simple than to distribute complexity evenly everywhere in some kind of open-coded swamp.
- timewizard 2y ago> Complexity has to live somewhere. If you are lucky, it lives in well-defined places. This whole section makes me think of construction which has similar abstraction and hidden complexity problems. It strikes me that they solve it by having design be entirely separate from implementation. Which is usually the corner where all our luck as software developers inevitably runs out. Our methods are still rather "cowboy." We have cool "modernized cowboy" languages that make it hard to shoot your foot off, but at the end of the day, we're still just riding old horses and hoping for the best.
- wmwragg 2y agoI've often thought this. It feels like there should be two languages, one for the implementation of the parts, and another to design/architect the software using the parts, allowing the design/architect language to focus on the high level architecture of the software and the implementation language to focus on the parts. We currently use the same language for both, and mix the two areas as we program
- deleted 2y ago[deleted]
- elcritch 2y agoTo be fair to our field fields like construction have literal millennia of history and development to figure out the best patterns. Even then it’s still evolving. It’s crazy to see what we’re capable of building now vs even 15 years ago.
- timewizard 2y agoConstruction is not built on a machine that has had exponential growth for those 15 years. So our capabilities have expanded but it seems like our problems have as well.
- est 2y agoI think "types" is the solution of two completely different problems: 1. how to specify memory layout for faster execution 2. how to give hint when I press . in IDEs if you use typing outside these two scopes you'd probably find many troubles.
- acchow 2y ago> if you use typing outside these two scopes you'd probably find many troubles. - encoding invariants and define valid evolutions of the codebase - memory safety without a garbage collector (see Rust’s Affine type system)
- grues-dinner 2y agoTypes imbue pure data with meaning. That's pretty much it, and the other uses of types flow from that. Whether you use that meaning to produce IDE hints (say, via Python type annotations, though I am aware Python typing isn't only that), or you feed it to a compiler that promises that it will ruthlessly statically enforce the invariants you set via the types, or anything else, is up to you, your goal and the language you use.
- mrkeen 2y agoThey also imbue code with meaning, not just data. For isn't, the return type STM () doesn't give you anything back, but it declares that the method is suitable for transactions (i.e. will change state, but can be rolled back automatically)
- hnlmorg 2y ago3. Compile time safety. That’s what I use types mostly for. I don’t care about compiler hints, well structured code with sane naming conventions solves that problem without the need for types. But I do want my program to fail to compile (or in JIT-land, fail unit tests / CICD) when I do something stupid with a variable. The former is about typing speed and I already type faster than I think. The latter is about guardrails protecting me from my own human error. And that is a far more realistic problem than my IDE performance.
- revskill 2y agoDoes complexity mean a long block of code with many levels of nested conditionals which are messed with cross-block mutable variables ?
- chuzz 2y agoThat's why Typescript/Python optional typing hit the best balance for me. Coding in duck-typed language is generally fine when your test suite is as fast and frequent as a type checker. That also explains why TDD is more popular in say Ruby or Python vs. Java. Speaking of Java, the problem with types is when you try to reify every single problem you encounter in your codebase. By the way, python has structured types since 3.8, and I hope they get more popular in Python code: https://docs.python.org/3/library/typing.html#typing.Protocol https://docs.python.org/3/library/typing.html#typing.Protoco...
- youerbt 2y ago> That also explains why TDD is more popular in say Ruby or Python vs. Java. I'd say that TDD being more popular in untyped languages speaks against TDD, as it hints that maybe some of its benefits are covered already by a type system.
- hitchstory 2y agoId say if you think tests and types are doing the same thing in the same way you are badly abusing at least one of them. One attacks the problem of bugs from the bottom up and the other from the top down. They both have diminishing returns on investment the closer they get to overlapping on covering the same types of bug. The haskell bros who think tests dont do anything useful because "a good type system covers all bugs" themselves havent really delivered anything useful.
- youerbt 2y agoI'm a Haskell bro and I love testing. You misunderstand me, though. All I say is that maybe _some_ of those tests deliver value by just making sure that code even runs, which is otherwise covered by types.
- hitchstory 2y agoWhen I do TDD (virtually every time i write a line of code) each test scenario isnt just a way to verify that the code is working, it's also a specification - often for a previously unconsidered edge case. Throwing away the test means throwing away that user story and the value that comes with it.
- lincpa 2y ago[dead]
- freehorse 2y agoI do not think of complexity as one thing. Abstractions are about both hiding and exposing complexity at the same time. Different levels of abstractions can expose or isolate different part of complexity. Exposing parts of it in a way that they become amenable to your tools is as important as isolating other parts somewhere in the background. Essentially, this has to do with how well a given abstraction choice maps into the structure of the problem-space and the relationships there. The choice of which parts of complexity you isolate and which you expose is important. You probably do not want to deal with everything at once, but also usually you cannot avoid dealing with something. The way I primarily see (and often like) type systems wrt complexity is as choosing which parts of complexity are important and exposing them (and rest being still there to deal with). There is a cognitive aspect to abstractions and complexity, irrespective even of IDEs, debuggers, compilers etc. I personally want my abstractions to make at least some sense in my head or a piece of paper in the way I think about the problem before even I start writing code. If the abstractions do not help me actually cognise about (some part of) the problem, they probably solve other problems, not mine.
- talles 2y agoThis is such a simplistic view on the matter. The author talks about complexity like it's always an intrinsic thing out there (essential) and the job of the abstraction is to deal with it. It misses the point that a great deal of the complexity on our plates are created by abstractions themselves (accidental). Not only that, sometimes great abstractions are precisely the ones that decide to not isolate some complexity and allow the user to be a 'power user'.
- titzer 2y ago> sometimes great abstractions are precisely the ones that decide to not isolate some complexity and allow the user to be a 'power user'. I agree with this. Sometimes abstractions are the wrong ones. In a layered system, where each layer completely hides the layer below, sometimes abstraction inversion (https://en.wikipedia.org/wiki/Abstraction_inversion https://en.wikipedia.org/wiki/Abstraction_inversion) occurs where the right mechanism is at the bottom layer but intermediate layers hide it and make it inaccessible, leading to a crappy re-implementation that is slower and usually less capable.
- JimmyWilliams1 2y ago[dead]
- layer8 2y agoI don’t think abstractions are inherently tied to hiding complexity. The purpose of an abstraction is to abstract over variations of a thing (think polymorphism), where each variation by itself might still be simple, or to separate essential features (e.g. parameters you have to pass) from accidental features (e.g. implementation details), where again there is no inherent implication of complexity on either side. In slightly different words, an abstraction separates what client code needs to reason about from what it should be able to ignore. Of course, if an abstraction isolates client code from certain complexities, that will contribute to the success of the abstraction. But it’s not the essence of what an abstraction does, or a necessary condition for it to count as successful.
- wavemode 2y agoThe author's assertion is true - complexity has to live somewhere. The nuance, though, is that all places complexity can live are not created equal. Let's take the example of memory management: by pushing that complexity into the type system, Rust forces the programmer to deal with it and design around it. At the expense of some performance, we could instead push this complexity into a runtime garbage collection system. Since the runtime system understands things about the runtime characteristics of the program that can't be proven via static analysis, it can also handle more things without the programmer having to intervene, thus reducing the difficulty of the programming language. For most programmers this is a positive tradeoff (since most programmers are not writing software where every microsecond matters). Similar tradeoffs exist in many different areas of software engineering. One monolith, where all the information is in one place, is easier to write code in than two microservices, which keep having to ask each other questions via API call. Yet, sometimes we need microservices. Rendering your web application entirely on the frontend in React, or entirely on the backend with templates, where all the logic lives in one place, is much easier than doing server-sided rendering then hydrating on the frontend. Yet, sometimes we need server-sided rendering and hydration. Complexity is an irreducible constant, yes, but cognitive load is not. Cognitive load can increase or decrease depending on where you choose to push your complexity.
- gorjusborg 2y ago(I'm not responding directly to parent post, more adding my two cents in agreement) 'Necessary complexity' needs to live somewhere. There is often a core of complexity that is intrinsic to the problem being solved. This cannot be removed, only moved/converted/etc.. That doesn't mean everything needs to be complex, and that you need to 'collect' it somewhere. There is such a thing as unnecessary complexity, and code that has a lot of it is 'bad code'. Don't fall for the trap of thinking that you can't improve code by identifying and eliminating unnecessary complexity.
- clarkmoody 2y agoThe other tradeoff comes with ease of debugging. Compile-time vs runtime errors. Dredging through microservice logs vs stack traces from the monolith.
- 2y ago
- IronRod 2y agoI find this topic particularly interesting. I've often said to others that software, in itself, is a general abstraction of one or more complex tasks. The whole point of software is to hide complexity and make possible, in a hopefully simpler manner, doing things that would otherwise be very difficult or impossible. Despite what users may experience, the complexity remains but becomes hidden.
- agentultra 2y agoIsolating complexity, I would say, is a consequence of using good abstractions... not necessarily the essence of abstraction however. The essence of abstractions are in semantics. I define a type and an algebra of relations on that type which gives me theorems I can use. That is the essence. The consequence is that I can now think in terms of the theorems and definitions I've established rather than all of the details at the lower-level. However, sometimes it's a bit over-rated when all that's needed is some information hiding and indirection, which is what this article appears to be discussing. These tools are the ones that are "leaky" in the sense that the complexity they attempt to hide often escapes the confines of their interface. It tends to give "abstraction" a bad reputation among programmers who have to deal with such systems. Essential complexity does have to live somewhere. Best to be upfront about it.
- deleted 2y ago[deleted]
- rapjr9 2y agoOne of the problems with abstraction is that while it hides complexity, it makes changes that must reach into that complexity difficult. Abstraction is great if the code is never going to change again. If the people using the code want new features, then abstraction is a barrier. Getting around the abstraction barrier makes the code more complex. You have to think about the entire life cycle of the code, not just what looks pretty when you first write it. Most developers have no idea what their code will be used for 5 or 10 years into the future. As an example people have been trying to abstract away the complexity of network connections for decades, without a lot of success in keeping the complexity hidden. Someone always needs direct intervention in a layer in the network stack to make their product work right.