I tend to agree with the author’s conclusion that, despite clocking in at 2000 words, this article barely scratches the surface. By the end of it, I am not yet convinced that the concept of degeneracy offers to me any new insight over simply striving to strike a balance between specificity and generality to aid adaptability, YAGNI, “elevator becomes useless but escalator just becomes stairs”-flavored metaphors for reslience etc. — but maybe I am too fixated on code (having just picked up John Ousterhout’s Philosophy of Software Design again).
That being said, since the author has spend years examining this idea, I’m sure he’s onto something. I just wish I could read further thoughs on the matter.
He gave a recorded talk on a similar topic:
This all seemed very abstract to me, until I realized that (if I understand correctly) I came across an example of degeneracy and its benefit just yesterday. Bear with me; this will take some explanation.
I’m working on a side project using the Phoenix web framework for Elixir. For this project, I need a form that makes changes to two database tables that aren’t directly related. (Maybe this is a hint that the database schema isn’t quite right. But I just want to get this UI done with what I have.) As is common for Phoenix applications, this one is using Ecto to interact with the database. When making a change to a database table, or related tables, you use an Ecto changeset. Phoenix has built-in support for using a changeset to validate and pre-fill an HTML form. I want that built-in support.
Notice, though, that with this built-in support, an HTML form can be based on only one changeset. I need to make changes to two tables that aren’t directly related. So I need two changesets, but in one form. The Phoenix.HTML.Form.form_for/4 function takes a changeset as one of its arguments, and it returns a complete form, with opening and closing tags. This seems to enforce the one-changeset-per-form limitation. So it would seem that I can’t do what I need and still get the benefits of the framework.
But wait, recent versions of phoenix_html have a form_for/3 function that doesn’t automatically wrap the form contents in opening and closing tags. This variant was added for integration with Phoenix LiveView, which doesn’t support the usage pattern used by form_for/4. It’s true that in the typical usage shown in the documentation, the return value of form_for/3 gets automatically converted to an opening tag which is added to the template. But the function doesn’t have to be used that way.
So by using the alternative form_for function in a creative way, I can render form elements and errors (if any) from more than one changeset, even though that’s not what the function was intended for. This is like the author’s one concrete example of degeneracy in computing, where an Exchange server running IIS can be repurposed to serve a static website. It kind of feels like cheating, but sometimes it’s very useful to be able to do something like this. And, to tie this back to the bit of trolling at the beginning of the article, in a more pure language or framework, this kind of repurposing might not be possible at all.
Designing a user schema made me think of “What’s the bare minimum I can get away with?”
I realized that in my prior experience, I worked with user schemas that had first name, last name, dob, organization membership, email, etc. But the more I thought about those fields, the more I felt a strong YAGNI pull. Names, for example, often do not neatly decompose into individual components. So I opted for the bare minimum of “fullname” to hold whatever the user chose as their name. Eventually I settled on (id, email, fullname, password_hash, created_by, created_date, updated_date) - the only truly immutable fields are (id, created_date, created_by), every thing else is mutable because I’ve no way to envision all use cases, but I did know what doesn’t change, at least for now. In some ways, that’s balancing degeneracy in terms of smaller constraints, requirements and assumptions.
(id, email, fullname, password_hash, created_by, created_date, updated_date)
(id, created_date, created_by)
I do like the article - I prefer the happy circumstance of “oh, we can adapt X, Y, Z for the CEOs latest idea” but I’ve also experienced the perils of overly specific architecture, making it difficult to pivot. I’m still not certain as to how to walk the architectural tight rope. Sometimes less information helps (avoiding overly constraining the design), sometimes more (determining the immutable characteristics early).
Interesting idea here. I think there’s something more here but I’m a bit skeptical, mostly because I’m always cautious of survivorship bias when trying to reverse engineer “best practices.”
Specifically, I don’t know how to feel about the motivating example. The devs mentioned there slap together something to appease upper management and admit it’s ugly. I don’t understand why I, as an outsider, should be impressed:
Adapting and reusing an existing component is a great idea. A really well-designed component can often be used in new contexts. Don’t think anyone would debate this is a great situation.
The story ending at, “it was a hack job but management was happy” makes it feel a bit too submissive for my taste.
Maybe I’m just grumpy.