Codebase Stewardship : Thought on making code bearable to maintain

Code is a piece of work, and also place where thing could out of hand to deal with. Such kitchen after cycle, You need keep it clean then you could work effectively.

After releasing Noa v0.2.0, I stumble plethora of trouble in doing codes from making code easy to understanding, analysing cost-benefit of feature I wrote and dealing with breaking changes. Also, not to forget how utilize Git to ease code versioning.

So do with code, here summarized lesson I've earn so far:

1. Redefining Code Standard

Noa written in Flutter. Flutter have known best practice name MVVM (Model-View-ViewModel) :

  • Model is area of code where you define data you want to handle and function to aiding it (e.g serialization, equality, validation, so forth), and storing it (e.g repositories, services).
  • View is place where serving user interface that will seen by, and place to serving data for user to have interact, but this not place for accesing Model, so
  • ViewModel, thing this place gluing between Model and View with logic, in this place where you code handler interaction from user, querying data from Model and anything you want to quering data and showing UI. In short, your app logic reside here.

That solid principle of Flutter code best practice. what if you feel overwhelm and confused with that? the option I do is make derivative of that principle to more dumb so I can understand (hopefuly others too).

In Noa, I breakdown the Model into different part, named Domain and Repository. Domain handle data definition and its function then repository used only for querying data for storing and querying only.

Then the ViewModel, I have changed to State because it literaly state for my UI changes. Also the View I've changed to Page, because it showing what I see like Web.

So, if I made an Expense feature then I would have folder structure like this.

+ expense
-   expense.dart
-   expense_repo.dart
-   expense_page.dart
-   expense_state.dart

This example from lib/expenses from Noa

This approach I've used known as domain-driven, which grouping a feature and it auxiliaries close together in one place.

TLDR: Suit best pratices by you own way.

Reference: Noa CODESTANDARD.md

2. Development Roadmap

There a poet said that, "a road not exist by no one walk on it", the answer is hope. Defining what feature you want to have in your apps does smilar like that, writing wishlist hoping that turn into realization (by do work, obviously).

Making roadmap works as vision for you program that you're develop, it to-do list of development progress.

I create Noa just a todo-list (felt recursive, huh?), but that too simple so I add more feature as basic feature, multi device, free of AST (Ads, Subscription, Tracker) and added unique value to my app as well. This is seed use-case that I use to build Noa roadmap.

Also it, will be place to find answer when doubt about implementation decision. Cost-benefit approach is good for determining whether you should add a feature or not.

For example, when you tempeted to making feature for support system tray in desktop platform but you app are target for multiplatform (stated in roadmap, Android and Linux), that feature will be costly (tackling platform-specific function and it dependencies) and also it nice to have since little benefit to core feature.

This also apply when a urge to adding notification much better since both platform supported, then again there no use case currently that need that, so it will be waste to do.

TLDR: Roadmap adding sense of direction to your development and reminder what you should be done. Think carefully what feature and design you want to have in your app.

3. Never delay breaking changes, unless nesscesary.

Breaking change is not only breaking interoperability across app version, it also happend to break the developer spirit. Like your code has debuffing effect even caffein boost got dispelled.

There case in Noa that I have do breaking change. When I use domain and sqlite at sametime, I not realize that I not orthogonally named the field, In domain, I name index field as "id" and "_id" column in databse scheme, I rename the database column exactly as index field and it repositories query to made serialization works smoothly.

Then why this called breaking change? Because user who earlier version use DB that index column named "_id", so the app current query will not able to find access it.

Whether it still possible to make recovery (I create separate branch to make export feature for older database or made database upgrade, the latter I lack due my ignorant about sqflite onUpgrade callback) but I find that will adding code smell if I put it into mainline, in other word it will sacrifice maintainability (I bit self-centered, heh?) and I sacrifice user convient.

I my bases of doing this is remembering that my apps still in < 1.0 version, that breaking change is expected. Also if my code above 1.0, I might making trangression version (such branch I made to relieve older db for user could export their data then migrated to newer version).

TLDR: Delaying breaking change will cause tech debt and impacting code maintainability, Hurrying solving breaking change without proper research inviting premature decision.

4. Documenting your codebase

All aspect I have been said above maningless without documentation of your codebase. After all, documentation is way to other and your future self to get grasp to understanding the code.

Start with README.md, then explain what purpose of your program currently develop, what feature it got, how to build and what dependecies they need in order to build, reference to other documents (such CODESTANDARD, License if any, Code of Conduct, CONTRIBUTING, API Documentations, Code of Conduct etc) that help you explaining what the codebase it is and how you govern it.

When you write code you wrote a program, when you documenting codebase you writing document that governing what should to do inside it, equivalently important like bill-of-law for you codes.

I ,personally, documenting what I need to such README.md (obviously), CODESTANDARD to put any code rules and customs in codebase, LICENSE/COPYING for licensing, CHANGELOGS to track progres and history of development (sometime I put this with README if it short).

TLDR: Write and documenting thing that help you understading the codebase and aid you in maintaining it.

5. Versioning

App development have checkpoints to ensure the features you deliver at that times, this checkpoints is versioning. Version in app should reflect what you want to deliver with the apps. This to notify your potential user what current state of the apps, is alpha or already beta even stable ? at what version the feature that I want most ? and so forth.

This also have tied with roadmap, that affecting how you should versioning. Like when you finish certain task in roadmap you increase the version, also when there a patch you increase the version even you finish the roadmap you increase the version.

There a lot way of versioning,like 0-based versioning and Semantic versioning , I use the latter.

Semanting Versioning have three main component : MAJOR.MINOR.PATCH

  • MAJOR when you doing big change such finish roadmap you listed.
  • MINOR when you doing small change such adding feature, remove one and so forth.
  • PATCH when you apply bug fix, patch or whatever to current existing app.

For example (CHANGELOGS):

  • Noa v0.1.2 : 0 <- roadmap still far, 1 <- I finish basic feature in roadmap, 2 <- I have apply fix unexpected behaivor in expense and todo feature.
  • Noa v0.2.1 : 0 (same as above), 2 <- I implemented export/import feature, 1 <- I have fixing the file picker bug.

So when you done with coded some feature and what to notify the user you release new version. Keep in mind, releasing version is big task you should ensure the feature well-tested before deploying.

Always keep track you version in CHANGELOGS and git tags that refer to commit of refered version.

TLDR: Checkpoint for developer works, Maturity level for user prespective.

Conclusion

This what I've learn so far from maintaining code. Differ to just writting program/apps, that you eventually will forget. One of goal from maintaining code is to keey you able get grasp the codebase no mater how long you leave it and other could understand it.

This log will be changing as far I learn about maintaining codebase, pretty sure I miss something that needed yet I haven't encounter, such how to contributing, how you take care yourself when mentally troubled with development and things related to software engineering.