Live data from Hacker News

Software Architecture Is Overrated, Clear and Simple Design Is Underrated

blog.pragmaticengineer.com

191–200 of 218 posts

Re: Software Architecture Is Overrated, Clear and Simple Design Is Underrated

#191
post #187
post #183

Earlier quoted context omitted.

Please be specific about the issues and let’s discuss. I am serious - this list is optimized for maintainability of code. Developer time is more valuable than processor cycles, in most cases. Unless you are the kind of person who would argue that C++ introducing object orientation and virtual methods made everything slower and that extra indirection by default is hilariously bad architecture?

> 3. Functions should have extensibility, put the required parameters as parameters and always include an “options” at the end. Each function can have defaults that you can extend, which means you need a deep-extend method: counter-point: why not write functions that take the arguments they need? if they need more arguments later, why not add them later? > 4. When in doubt whether to do convention A or B, take a bit…

3. Because it explicitly signifies a place to put those later arguments, in a way that is MAINTAINABLE.

First of all, the last parameter should have a default value of {} — that is, no options, but can still be deferenced in code. So not passing options is ok.

Secondly, if you don’t do this, future developers will keep adding parameters in an ad-hoc manner until you get stuff like:

context.copyImage(src, dest, sx, sy, sw, sh, dx, dy, dw, dh, filter, rotation, matrix, ...)

and your calls will keep looking like:

context.copyImage(a, b, 0,0,30,30, 20,20,40,40, null, 5, null, “foo”)

Not only will it be harder to read for anyone looking at the calls, but also the future function signatures will have parameters in the chronological order they were added — which is often totally unrelated to the order they should be in, but you can only add them at the end.

Since the function should be backward compatible, all new parameters are by definition OPTIONAL and therefore can be added to a hash or object called “options”.

And YES I stand behind this. Years ago I actually recommended this to the PHP language mailing list:

https://grokbase.com/t/php/php-internals/1042mr8yrn/named-pa...

In other words, I wanted the function call syntax in PHP look like the array syntax:

func($a, $b, $c => 3, $d => “foo”);

Simple, and elegantly enforces the above convention while looking “quintessentially” PHP!

4. When you are building a re-usable framework, it’s silly NOT to anticipate future needs. The whole point of a framework is future needs.

Now, you say you should weite code rhat is easy to undeerstand and change. If you hardcofe vales, that’s easy to change - just put a variable there. But if you hardcofe a concention in 100 places, fhat’s not ao easy to change. You can’t just grep for a hardcofed constant.

But ot gets worse rhan that. Other code will come to rely on this “invariant”, which may change later. Again, the wholw goal of my recommendations is to future-proof your code so that future developers will write code rhat grows up around it and can go in any direction, and can play nice with each other.

5. Suppose you are lacking middleware between A and C. So now you want to mock an input from A. Too bad, you can’t. Ok, what if you want to modify something that goes on between A and C? You have to rewrite A or C.

Let me give a real example. Suppose I said that people may have one more articles they write. So I implement a User table and an Article table, which has authorUserId field on it. Simple, right?

Except it’s too simple - one article can at most one author. If instead I had thought ahead and made three tables: Author, Article, and Authorship as a join table with articleId, authorId, then I could have 1 to N mappings in both directions and far more flexibility.

Now you may say — why think ahead? Maybe in the future articles can have more authors and THEN we will refactor the code! Except at that point you’ll have tons of plugins and apps, some beyond your control to change, relying on the details of your implementation.

Of course, you should also use another principle I didn’t mention (because it’s very obvious and popular) namely to write abstract interfaces that don’t leak implementation details, and keep these interfaces as small as possible, so that you can reason about large systems through these “bottlenecks”. But I have found that, on the back-end, it’s just a bit of extra work to add an extra indirection, whether you use it now or not. Instead of saying “we will NEVER do it the other way because it makes no sense”, if it costs you so little, why not allow for it, in case later someone will want it? The interfaces are often a leaky enough abstraction for this to matter.

For example you’d have article.getAuthor() if you didn’t make that extra table join indirection. And now what will article.getAuthor() return when articles can have many authors? It would return a random author, for backward compatibility. With my approach, you would prevent the “older” apps from using dumber interfaces. It’s just a bit extra work for everyone, for huge wins later. And that’s the point.

7. Event streams can be abstracted into pipelines and middleware. You can do undo/redo, store histories, have Merkle Trees and more. Compare SVN and Git :)

8. Sync becomes much easier when everything has a history of states. Look at git. You can just use it. Look at scuttlebutt, blockchains, or other types of merkle trees. Everything becomes super simple to reason about.

This forum could be refactored to be distributed. Everyone owns their own node of the tree and the relationship to its children (replies), and everyone else just replicates them scuttlebutt-style. Expanding a tree is fairly simple, and each branch has a merkle hash. There is no central server.

And the best part - you could start with a centralized app and gradually move to a decentralized, end-to-end encrypted model, if you only had the foresight to make sure that your tables has primary keys corresponding to how people look up some data (node, etc.)

The only thing you’d need to have consensus about is the ordering of replies to a node. And that consensus can be among the repliers or simply dictated by the parent node’s owner.

8. Having a non-extractable private/public key be used to sign requests is better than JUST having a bearer token (cookie). If someone commandeers the cookie via, say, session fixation, they still won’t have your private key. But they need the session id (bearer token) to look things up on the server. This is “piling on”.

Then, on top of this, you can have a blockchain of keys for devices, stored across sites, so you can revoke a device when it’s compromised, or authorize some new device with N previous devices.

You can have the same exact mechanism manage users in a chatroom or other merkle tree structure. This is what keybase does.

You can encrypt data on the server, with people’s public key, and they have to decrypt it.

You have to make sure that the initial signup requires some sort of token to prevent sybil attacks.

In short — once again the approach is to “layer on more security mechanisms”, they should all work independently.

You don’t just rely on HTTPS for example, because a server or CA cert can be compromised. You hash passwords on the client with salts before sending, regardless. Once again this is called “defense in depth”.

Re: Software Architecture Is Overrated, Clear and Simple Design Is Underrated

#192

Earlier quoted context omitted.

Basic dependency injection is just functional style - code getting its dependencies as arguments. I feel it's often actually simpler than having code manage its own dependencies. I didn't think that until recently, though, because my primary exposure was always bloated Enterprise Java DI frameworks written in pre-Java 8 style. I'm not saying the frameworks were bad per se, just that the amount of incidental bloat pre…

I gave DI as an example, but other types of abstractions can also make an implementation more complex (i.e: less simple) but easier (or just make it possible) to test, which can be quite important (dependending on the context of course). Now for DI being a functional programming principle, I don't know, I guess you could argue for this. I personally learned it as a way to satisfy the "D" from the SOLID principles, so…

Depending how you look at it, you could argue that classical approach to SOLID's dependency inversion is an amalgamation of two separate concerns - dependency injection for ensuring that neither "higher" nor "lower" level depend on the other directly, plus a type system restricting what operations are available to both. There are so many ways to look at and reason about the problem of structuring programs that pretty much every year I discover a new perspective on an old thing that blows my mind.

Now my enlightening moment about dependency injection was this: it's literally as simple as passing an argument to a function. In a functional approach, you may be passing lots of values and closures expressing dependencies with surgical precision, in an object-oriented imperative approach you might pass an instance conforming to an interface just once. But it's the same concept.

Re: Software Architecture Is Overrated, Clear and Simple Design Is Underrated

#193
post #71

I have a hard time understanding the author's point of not using UML but somehow boasting that they used "plain old boxes and arrows" to create "plenty of diagrams". UML is nothing more than a bunch of "plain old boxes and diagrams", but which have concrete, objective meaning that has been specified and thus help share ideas as objectively as possible. UML is a language, and languages are tools to communicate and sha…

Because UML is generally about defining processes, and it is easy to accidently try to poorly "code" parts of the system in UML, processes that might be easier represented in code. If there is distinct process that is complex/important enough to be architected, by all means use UML. Normally, at a high level, where people are architecting, what is more important is flow of information and containment of responsibilit…

> Because UML is generally about defining processes

It really isn't. In general UML specifies diagrams for relevant system views, but it's centered around diagrams that represents the structure of software projects, not processes. Perhaps UML's most popular diagram is the class diagram, which is complemented with the component diagram and deployment diagram. UML modeling software focuses mostly on structural diagrams, whether to generate source code or dump DDLs. Most of the diagrams used to directly or indirectly represent processes, such as sequence diagrams and communication diagrams, are hardly known and far from popular. Flow charts/activity diagrams are hardly seen as UML, and UML doesn't even provide anything similar to the age old data flow diagrams.

Re: Software Architecture Is Overrated, Clear and Simple Design Is Underrated

#194
post #170

Earlier quoted context omitted.

Over my career, I've worked with engineers that like to over-engineer and under-engineer. The over-engineered code looked like russian dolls: had many layers to it, and some of the abstractions offered no value. That can make onboarding to such code unnecessarily complex. On the other hand, under-engineered code made very little of use of even simple data structures or algorithms. I like to call it "chicken scratch"…

In general I have found — over 20 years of experience architecting software - the following: 1. Platforms and reusable frameworks should be architected as well as possible. Apps can be whatever. 2. A developer who writes clean code and documents it is far better than a “10x” developer, unless you have budget for only one developer. 3. Functions should have extensibility, put the required parameters as parameters and…

> 3. Functions should have extensibility, put the required parameters as parameters and always include an “options” at the end. Each function can have defaults that you can extend, which means you need a deep-extend method:

No! Absolutely not! There is a time and a place for this, but it's nearly impossible to reason about the interface if any data can be passed in.

Re: Software Architecture Is Overrated, Clear and Simple Design Is Underrated

#195
post #5

First 1-3 years of coding, I just coded to get sht done. I got a lot of sht done. Next 4-8 years, I started getting cute with it and applied all kinds of design patterns, Factory, Abstractions, DI, Facade, Singleton you name it. It looked cute and felt good when it all worked but it was a juggling act. There was usually like 2-3 files to touch just to do one thing. UserFactory, UserService, UserModel, User, you get t…

Most devs prepare for the abstraction nirvana. I see a lot of fellow devs creating complicated code, because "in case we need to switch out the database down the road" or "what if we want to run the web app in CLI" In 20 years of programming I maybe seen one or two times a large application switched database engines and I've never seen a client want to run his/her web application in CLI... The art in programming is t…

I'm not sure that was the best example to use of an abstraction that leads to more complicated code, with database code I feel like it's way simpler to have it separate from your business logic. That way, there's fewer places where you need to make changes anytime there's a schema change.

Abstracting away the database also makes it easier to write unit tests for your business logic.

Re: Software Architecture Is Overrated, Clear and Simple Design Is Underrated

#196
post #5

First 1-3 years of coding, I just coded to get sht done. I got a lot of sht done. Next 4-8 years, I started getting cute with it and applied all kinds of design patterns, Factory, Abstractions, DI, Facade, Singleton you name it. It looked cute and felt good when it all worked but it was a juggling act. There was usually like 2-3 files to touch just to do one thing. UserFactory, UserService, UserModel, User, you get t…

I've been on a similar journey, and I've seen this pattern repeat itself again and again!

1. Hack any old shit together, but it works

2. When you actually have to maintain what you previously wrote, you realise (1) doesn't work so well. Then design patterns seem like an epiphany, and you Cargo-cult the shit out of them, using them everywhere. You dogmatically eliminate all code duplicatation, use mocks with wild abandon, and are not happy unless you have 100% test coverage. For bonus points, you also overuse abstraction. A lot.

3. When you actually have to maintain anything you previously wrote, you realise what a tangled mess of abstraction you have made - you can't simply open a file and ascertain what it's doing! You also realise that the tests you wrote to achieve 100% coverage are crap, and don't really prove anything works. You finally reach a zen-like state, realising that simplicity is key. You shun all forms of dogma, and use patterns and abstraction, but only just enough

Re: Software Architecture Is Overrated, Clear and Simple Design Is Underrated

#197

Earlier quoted context omitted.

>started getting cute with it and applied all kinds of design patterns Even though there are books about design patterns, taking such a book and trying to "apply" its patterns is a bit backwards I think. The idea of patterns is they describe commonly useful solutions, not designs you "should" use. Once you started to code in "pragmatic, minimalistic way" I assume you found you could apply the same solutions you had f…

I agree with you, but that's not how people usually progress. Its more in line with initial discovery of 'the best and battle-tested way to design code'. Immediately they try to apply patterns anytime they see an opportunity for it. They must be taken more seriously from now on, right? I get it, I went through exactly same hoops. My guess is, we all desperately want to be those aged and wise devs that nail the implem…

It is understandable that everybody wants to learn especially junior developers, and it is a good thing to learn, and what better way to learn than try out different things.

Unfortunately then rather than getting something useful done we often just get some learning done, perhaps learning how NOT to do it :-)

The same issue I think affects the tools landscape. People want to use the latest hyped things because ... they want to learn how to use the new tools. The new tools might not be better, but you don't know until you try them.

Where you make a great point I think is that the most important thing to learn is: The simplest solution that works is typically the best. They used to say "YAGNI", You Ain't Gonna Need It.

Re: Software Architecture Is Overrated, Clear and Simple Design Is Underrated

#198
post #170

Earlier quoted context omitted.

In general I have found — over 20 years of experience architecting software - the following: 1. Platforms and reusable frameworks should be architected as well as possible. Apps can be whatever. 2. A developer who writes clean code and documents it is far better than a “10x” developer, unless you have budget for only one developer. 3. Functions should have extensibility, put the required parameters as parameters and…

> 3. Functions should have extensibility, put the required parameters as parameters and always include an “options” at the end. Each function can have defaults that you can extend, which means you need a deep-extend method: No! Absolutely not! There is a time and a place for this, but it's nearly impossible to reason about the interface if any data can be passed in.

I think you misunderstand. It’s not that “any” data can be passed in. The options object is documented in every version. It’s just a place that lets future versions add named parameterd

Re: Software Architecture Is Overrated, Clear and Simple Design Is Underrated

#199
post #191
post #187

Earlier quoted context omitted.

> 3. Functions should have extensibility, put the required parameters as parameters and always include an “options” at the end. Each function can have defaults that you can extend, which means you need a deep-extend method: counter-point: why not write functions that take the arguments they need? if they need more arguments later, why not add them later? > 4. When in doubt whether to do convention A or B, take a bit…

3. Because it explicitly signifies a place to put those later arguments, in a way that is MAINTAINABLE. First of all, the last parameter should have a default value of {} — that is, no options, but can still be deferenced in code. So not passing options is ok. Secondly, if you don’t do this, future developers will keep adding parameters in an ad-hoc manner until you get stuff like: context.copyImage(src, dest, sx, sy…

> Secondly, if you don’t do this, future developers will keep adding parameters in an ad-hoc manner

You seem to presume a situation where you have absolute control over the initial signature of functions added to the codebase but no ability to constrain future changes.

1. All parameters that for which a default makes sense should be optional and have a sensible default.

2. (In languages where this is an option) All parameters with a default must be keyword-only.

3. All new parameters to an existing function (from a stable release) must have a sensible default.

> When you are building a re-usable framework, it’s silly NOT to anticipate future needs. The whole point of a framework is future needs.

The point of a framework is to avoid solving the same problem multiple times in each new project. If you haven't needed to solve it twice in two separate projects, be skeptical that you need it in a framework. If you haven't needed it once, don't even consider it. Code for problems you don't have is pure waste.

Much of the advice you offer is going to produce waste because YAGNI; each time they come up the cost may be small, but in aggregate it's going to be a lot of extra zero-value code being written and maintained, bloating development and maintenance costs and timelines. Occasionally, you'll be benefit a little down the line from hitting a problem you correctly anticipated, but often you’ll suffer from having not having anticipated the real constraints of the problem when dealing with it when it wasn't a real need, meaning you’ll have to throw away your just-in-case code anyway, and all the time you'll be dealing with extra complexity dealing with problems you haven't had any real need to address but only imagined might come up in the future.

Re: Software Architecture Is Overrated, Clear and Simple Design Is Underrated

#200
post #63

Let’s see how the OP’s system looks in 20 years. Then we’ll see how clear and simple it has remained. The OP is railing against a culture that never existed. Banks software architects are not in their offices smoking cigars and making UML diagrams that they send to coders, only to realize later that they made the wrong trade off. What happens is: You design a system for what it’s supposed to do. You do it the way the…

> Banks software architects are not in their offices smoking cigars and making UML diagrams that they send to coders,

You'd be surprised at how common this is, especially in large companies that play, "let's pretend to do technology." I'm leaving a large hospital where I've spent half my time butting heads with our "architect" who's skills have been frozen since 2005. Leadership is all eager to chase modern buzzwords like "machine learning" and "AI" but this guy is advocating for outdated crap.

Post reply on HN