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