Refactoring to Web Components
My initial release of Jernel was basically just one big function that contained all of the DOM-building, and all of the event handling. It was functional, but not maintainable.
My next steps after that were to add automated tests, and then I also added a little inline editing functionality within my single function app.
Refactoring a messy function into web components
Now, I've refactored all of the rendering and logic into custom elements. I find vanilla JS custom elements to be a nice starting point for a small app. I can easily extract one little element at a time from my larger system.
In this case, I started from the inside and worked my way out. I first replaced a <span> with a <note-viewer>, and replaced a <textarea> and <button> with a <note-editor>. I then encapsulated a little more logic - toggling edit mode and deleting a note - in an <editable-note>. Finally I extracted the remaining outer logic into a <notes-app>.
I started from the inside out, but I could have just as easily started outside-in - with the <notes-app> shell first.
Why custom elements?
I used simple vanilla custom elements throughout, with no frameworks or other dependencies yet, (except happy-dom for testing). Custom elements are ideal in cases where you have a bit of a mess of JS and HTML, and want to start encapsulating some small bits to make it more modular. They don't require you to upgrade your whole app at once, and they play nicely with vanilla JS, as well as with most frameworks.
I'm pretty comfortable with vanilla custom elements though. But, they can be a bit more tedious than other options (once you have those configured well). They're not for everybody. When I start a project like this, I prefer to minimize dependencies, and to minimize total bundle size. I have a working little app in less than 10kb of code - and that's before any minimization or compression. With gzip it comes to less than 3 kb. My app loads in about 500ms, and my unit tests run in about 600ms.
I could have gone directly to React or Vue or something similar. Either at this refactoring step, or at the outset. But, those both bring an additional layer of tooling overhead and indirection. They can be helpful, but they also add complexity and maintenance costs. And, even though they're pretty fast for what they do, they'd add at least a little time to each iteration of my build and test loop.
You don't have to start with custom elements, or vanilla JS though. Pick the tool/ecosystem that you're comfortable with! And, pick the tradeoffs that you're comfortable with!
Still working in small steps
All of this combined took me a little longer than I expected, probably about 2.5 hours total. But, each individual step was less than an hour to extract a component, connect it into the app, and ensure that the app and all of the tests still worked. So, that all still fits nicely within our goal of working in very small increments.
Should I have started here?
If I knew what I was building, might it have been more productive to simply start with web components, rather than building an obviously unmaintainable version first? Maybe. But the iterative process removed some of the pressure of figuring it all out up front, or getting it all right at once.
There is always more than one way to do something. Sometimes tiny iterations make it easy to just make steady progress without too much stress. Other times it's worth taking bigger steps earlier.