This commit is contained in:
parent
435ec21f6e
commit
5d631561b5
4 changed files with 119 additions and 51 deletions
58
flake.lock
Normal file
58
flake.lock
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
{
|
||||||
|
"nodes": {
|
||||||
|
"flake-utils": {
|
||||||
|
"inputs": {
|
||||||
|
"systems": "systems"
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1710146030,
|
||||||
|
"narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=",
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"id": "flake-utils",
|
||||||
|
"type": "indirect"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixpkgs": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1718089647,
|
||||||
|
"narHash": "sha256-COO4Xk2EzlZ3x9KCiJildlAA6cYDSPlnY8ms7pKl2Iw=",
|
||||||
|
"owner": "NixOS",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "f7207adcc68d9cafa29e3cd252a18743ae512c6a",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"id": "nixpkgs",
|
||||||
|
"type": "indirect"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": {
|
||||||
|
"inputs": {
|
||||||
|
"flake-utils": "flake-utils",
|
||||||
|
"nixpkgs": "nixpkgs"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"systems": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1681028828,
|
||||||
|
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": "root",
|
||||||
|
"version": 7
|
||||||
|
}
|
|
@ -1,51 +0,0 @@
|
||||||
---
|
|
||||||
title: Coping with refactoring
|
|
||||||
date: 2023-09-02T02:17:23.405Z
|
|
||||||
tags:
|
|
||||||
- software-engineering
|
|
||||||
|
|
||||||
heroImage: ./ruinsHero.png
|
|
||||||
heroAlt: ruins
|
|
||||||
draft: true
|
|
||||||
---
|
|
||||||
|
|
||||||
It is the inevitable nature of code to be refactored. How do we make it a less
|
|
||||||
painful process?
|
|
||||||
|
|
||||||
It pains me to start a stream-of-consciousness type of article with a
|
|
||||||
definition, but to set things straight let's be sure we're talking about the
|
|
||||||
same thing. When I say **refactoring** I mean changing potentially large parts
|
|
||||||
of the codebase purely for the sake of making it more "organized". For some
|
|
||||||
definition of organized.
|
|
||||||
|
|
||||||
As software developers, we usually think of refactoring as something we do in
|
|
||||||
order to make something easier. A common example would be something like a few
|
|
||||||
lines of code that people on your team have just been copy and pasting
|
|
||||||
mindlessly everywhere, because to make it generic would mean that they would
|
|
||||||
have to touch code outside of their little bubble and then reviewers get
|
|
||||||
hesitant at the diffs and yadda yadda all kinds of problems supposedly.
|
|
||||||
|
|
||||||
Now it's your turn, and you have to change something tiny in those few lines of
|
|
||||||
code ...everywhere. Before you go in and start abstracting all of it into
|
|
||||||
something more generic, take a breather and think for a second: is it worth it
|
|
||||||
to refactor?
|
|
||||||
|
|
||||||
If your refactor involves adding some extra helper classes or you're pulling out
|
|
||||||
your toolbelt of design patterns, **you are creating complexity**. And in the
|
|
||||||
software world, complexity is the real devil.
|
|
||||||
|
|
||||||
Many people try to code in an "extensible" way in order to avoid refactors, with
|
|
||||||
extravagant interfaces and inheritance patterns. But all they've created is just
|
|
||||||
a larger mess that's harder to clean up later down the line when it eventually
|
|
||||||
needs to be rewritten. And it _will_ eventually need to be rewritten.
|
|
||||||
|
|
||||||
Let's talk about object-oriented programming. There's this bizarre
|
|
||||||
[open-to-extension but closed-to-modification][1] principle I've observed where
|
|
||||||
people are so resistant to changing their source code that they'd implement
|
|
||||||
heaps of useless design patterns on top of it in order to keep their little
|
|
||||||
classes from ever being touched.
|
|
||||||
|
|
||||||
[1]: https://en.wikipedia.org/wiki/Open%E2%80%93closed_principle
|
|
||||||
|
|
||||||
If that doesn't sound insane to you, let's take a look at a case study. Suppose
|
|
||||||
you're writing some code that takes in a request type,
|
|
|
@ -0,0 +1,61 @@
|
||||||
|
---
|
||||||
|
title: Coping with refactoring
|
||||||
|
date: 2024-06-21T05:56:50.594Z
|
||||||
|
tags:
|
||||||
|
- software-engineering
|
||||||
|
|
||||||
|
heroImage: ./ruinsHero.png
|
||||||
|
heroAlt: ruins
|
||||||
|
---
|
||||||
|
|
||||||
|
It is the inevitable nature of code to be refactored. How do we make it a less
|
||||||
|
painful process?
|
||||||
|
|
||||||
|
A not-horrible approach to creating a piece of software by first developing the
|
||||||
|
happy path, and then adding extra code to handle other cases. When we do this,
|
||||||
|
we may find that patterns emerge and some parts may be abstracted out to make
|
||||||
|
the code cleaner to read. This makes sense.
|
||||||
|
|
||||||
|
It seems that many engineers decided that this process of abstracting is too
|
||||||
|
painful and started using other people's abstractions pre-emptively in order to
|
||||||
|
avoid having to make a lot of code changes. They may introduce patterns like the
|
||||||
|
ones described in the GoF Design Patterns book.
|
||||||
|
|
||||||
|
Some abstractions may be simple to understand. But more often, they almost
|
||||||
|
always make the code longer and more complex. And sometimes, as a part of this
|
||||||
|
crystal ball future-proofing of the code, you may make a mistake :scream:. At
|
||||||
|
some point, you will have to change a lot more code than you would've had to if
|
||||||
|
you didn't start trying to make a complex design to begin with. It's the exact
|
||||||
|
same concept as the adage about [premature optimization][2].
|
||||||
|
|
||||||
|
[2]: https://en.wikipedia.org/wiki/Program_optimization
|
||||||
|
|
||||||
|
As an example, as a part of one of my previous jobs, I was reviewing code that
|
||||||
|
created _10+ classes_ that included strategy patterns and interfaces. The code
|
||||||
|
was meant to be generic over something that could be 1 of 4 possibilities. But
|
||||||
|
the 4 possibilities would basically never change. The entire setup could've been
|
||||||
|
replaced with a single file with a 4-part if/else statement.
|
||||||
|
|
||||||
|
I'm not saying that design patterns aren't useful. If we had more possibilities,
|
||||||
|
or needed to make it so that programmers outside our team had to be able to
|
||||||
|
introduce their own options, then we would have to rethink the design. But
|
||||||
|
changing an if statement in a single file is trivial. Changing 10+ files and all
|
||||||
|
the places that might've accidentally referenced them is not.
|
||||||
|
|
||||||
|
Some people think they can dodge the need to refactor by just piling more
|
||||||
|
abstractions on top, in a philosophy known as ["Open to extension, closed to
|
||||||
|
modification."][1] I think this is just a different, more expensive form of
|
||||||
|
refactoring. Increasing the number of objects just increases the amount of code
|
||||||
|
you need to change in the future should requirements or assumptions change.
|
||||||
|
|
||||||
|
[1]: https://en.wikipedia.org/wiki/Open%E2%80%93closed_principle
|
||||||
|
|
||||||
|
So the next time you're thinking of introducing design patterns and creating a
|
||||||
|
boat load of files to hide your potential complexity into, consider whether the
|
||||||
|
cost of adding that abstraction is worth the pain it will take to change it
|
||||||
|
later.
|
||||||
|
|
||||||
|
> [!NOTE]
|
||||||
|
> As a bonus, if your language has a good enough type system, you probably don't
|
||||||
|
> need the strategy pattern at all. Just create a function signature and pass
|
||||||
|
functions as values!
|
Before Width: | Height: | Size: 2.4 MiB After Width: | Height: | Size: 2.4 MiB |
Loading…
Reference in a new issue