Tech stack lessons from my 7+ year indie dev journey

Tech stack lessons from my 7+ year indie dev journey
A dog trying to adapt to technological innovations

It's been 7 years since I've been developing Inkdrop, a Markdown note-taking app. Thanks to its sales, I've been able to make a living from it.

In the past, I've written various things about how to sustain indie development from a business or mental perspective, such as "Get A Slow Tempo — Towards Becoming A Long-Running Product" and "I stopped setting a financial goal for my SaaS." This time, I'd like to share my thoughts on the technical aspect - how to keep developing sustainably over the years, focusing on the technology side.

TL;DR

  • Initially, aim to release as fast as possible when choosing a tech stack
  • When in doubt, choose what "sparks joy"
  • Cut it off at a reasonable point and proceed with development
  • Be prepared for your modules to become deprecated/obsolete
  • Old doesn't necessarily mean bad
  • Simplify
  • Learn longevity secrets from long-standing companies called 'Shinise'
  • You can't fully eliminate the element of luck

Initially, aim to release as fast as possible when choosing a tech stack

Development plans and business plans are inseparable. It seems that many solo developers become so engrossed in coding that they fall into perfectionism and end up unable to release, abandoning the project. I understand the desire to create something perfect from the start to amaze everyone. But let's give that up. If you want to make a living through solo development, you must first achieve PMF (Product Market Fit).

PMF essentially means confirming whether your app can make money or adjusting its direction to make it profitable. To achieve PMF, you must release first; otherwise, nothing will start. Just thinking about it won't lead to monetization.

To release as quickly as possible, don't be afraid to adopt the programming language you're familiar with, convenient libraries, frameworks, or templates you already have. Don't worry now whether it's the best long-term choice. The top priority is to create an MVP (Minimum Viable Product) and ship it to the world.

The early days of Stripe (Source: Quora)

The first version of Stripe was very rough, and MailChimp v1.0 was also extremely simple. That's fine. Inkdrop's initial version didn't even have a proper settings screen, as I wanted to release it quickly. So I postponed implementing payments and posted on Hacker News to announce a closed beta to recruit testers and gauge reactions.

I'm not saying to make something sloppy. Your app should solve a problem. I'm saying to create the minimum necessary to solve that problem.

Ask yourself

  • What problem does your app aim to solve?
  • What's the quickest way to implement a solution to that problem?
  • Is what you're currently doing related to solving the problem?
  • Are you spending time impressing others instead of solving the core issue?
  • Do you really need to refactor right now?
  • Are you procrastinating on releasing out of fear?

When in doubt, choose what "sparks joy"

Especially in the early stages, development is a continuous series of choices on which tech stack components to adopt. You may often find yourself struggling with these choices.

I'm frequently asked, "Which is the best, A or B?" For example, "React Native or Flutter?", "Vim or VS Code?", "React or Vue?", etc. These questions often seem to come from beginners, posed without any context or requirements.

Just as there's no single best life choice for everyone, there's no universally best technology choice either. It's like asking, "Which country (or city) is best to live in?" If someone insists, "You absolutely MUST use XXX!", be wary of their vested interests.

So what criteria should you use to choose? In my opinion, it's whatever "sparks joy" for you. Go with whichever option feels "fun," "enjoyable," or "satisfying" to use, or has a good community vibe, a cool website - anything that intuitively makes you think, "This is great."

Motivation is crucial for solo development. You won't have teammates to encourage you. Conversely, you're free to choose without having to care about the team's opinions. Since you're doing solo development, you might as well pick the option that excites you - it'll be easier to keep going with that momentum. The choice that allows you to keep developing and maintaining your app is, I think, the "best" for you.

Spark Joy? I Don't Understand. Any concrete comparison criteria?

That said, there may be people who don't seek "excitement" from technologies or have never felt excited about them (though I question whether such people are well-suited for indie development). Let me list some relatively objective criteria for comparison:

  1. Quality of official documentation
    • No matter how new or trendy something seems, it's useless if there's no documentation to refer to when you're stuck.
    • Action: Get in the habit of reading official documentation.
  1. Constructive nature of the community
    • Skim through GitHub Issues, forums, and the author's activity on X (Twitter), etc.
    • Avoid projects where bug reports have been ignored for ages or discussions derail into irrelevant topics.
    • Action: When you encounter issues, get in the habit of checking the official Issues and the author's recent activity.
  1. Activeness of development
    • Dismiss modules whose repositories haven't been updated in six months to a year.
    • Check the 'Insights > Contributors' tab to get a timeline of commit activity and contributor churn (e.g., React Native).
    • Action: Technology ultimately comes down to people. Focus on the people.

The number of stars tends to reflect novelty or trendiness rather than sustainability, so don't rely on that.

Still, you ultimately can't be sure. Maintainers could get fired or change jobs and no longer have time for their OSS projects. Natural disasters or wars could also impact things. What started out simple could become overly complex and buggy through version upgrades. Your Pull Requests could be ignored. On the other hand, some exceptional people like Evan You or Marijn Haverbeke may defy all negative expectations.

So even after checking the above, it'll only improve your odds by around 5-10% at most. Just keep that in mind.

Cut it off at a reasonable point and proceed with development

The more you research this stuff, the more confused you can get. You could try out all the candidate stacks one by one and write up Pros & Cons, only to conclude, "Well, there's no 'best' after all..." and end up even more uncertain.

It's better to just make a rough evaluation, go with your gut feeling, and proceed with implementation. The time spent learning by doing will benefit both you and your users more. Sometimes, ignorance can be a driving force. Don't be afraid to not know everything. It's better to "learn by trying" rather than "learn before trying."

I believe the "power of ignorance" can sometimes be useful because knowing too much can paralyze you. Not knowing isn't inherently bad.

So pick something exciting, try it out, gain expertise through trial and error, and reevaluate your choice if needed based on that experience.

Be prepared for your modules to become deprecated/obsolete

Once you've released your app, confirmed it's likely to make money, and settled on a direction, it's time to improve quality. At this stage, there's a good chance the libraries and frameworks that were fresh when you started developing have already become obsolete. Or the framework you're using may have had a major version upgrade that broke backward compatibility. In other words, you're already saddled with technical debt. From my experience, this is unavoidable.

Here, trying to find technologies that will "absolutely never become obsolete" is a mistake. Unfortunately, no one can predict the future. For example, the Atom Editor was incredibly popular 7 years ago, but no one foresaw it being discontinued. Inkdrop was based on Atom's internal modules, so I've had to gradually replace them with alternatives (still not done).

Conversely, React Native was called an "absurd framework" when it launched, met with skepticism. But now it's adopted by Meta, Microsoft, Amazon, Shopify, Discord, and others. Still, no one can say for sure if it'll never become obsolete.

You should first acknowledge this reality.

Old doesn't necessarily mean bad

Even old, withering libraries can sometimes keep getting maintained, like jQuery. Or a library might have reached a point of completion and no longer needs maintenance, like Semantic UI. It's wrong to assume everything old should be replaced just because it's old. If it's not actually causing issues, keep using it.

For example, Inkdrop uses Semantic UI as the UI framework, and some of its components depended on jQuery. As a note-taking app that needs to be highly functional yet lightweight, I needed to reduce such redundant dependencies. So I replaced only the jQuery-dependent parts with my own minimal React implementations. Since it didn't cause any functional issues, it was a very low priority - mostly just self-satisfaction.

You should avoid reinventing the wheel as much as possible. It's tricky, as you can unconsciously start doing it. After reading the source code of projects like Headless UIRadix UI, and React Aria, I strongly realized that if I casually started reimplementing something like "Just another dropdown," I'd inevitably fall into a bottomless pit. I'd want to punch myself.

Simplify

Products made with a focus on rapid release and speed of feature addition will inevitably end up with redundancies and bloat everywhere. However, once you've achieved PMF and confirmed the business is viable, start refactoring to strip away anything unnecessary. I don't recommend spending too much time on refactoring before achieving PMF - that's not the core issue. Also, around this time, performance will start to matter, and you may receive complaints from users. Consolidate duplicate dependencies across versions, remove unused modules, and understand the internals of libraries you'd previously treated as black boxes to eliminate wasteful processing.

On the other hand, dedicating time to refactoring will inevitably slow down your perceived development speed from the outside. You may get scolded, "I can't wait any longer!" Out of a sense of customer service, you might be tempted to hastily tack on shiny new features, but resist that urge. As frustrating as it feels, don't forget to prioritize making your existing happy customers even happier over acquiring new users. Adding features exponentially increases complexity. Are you prepared to keep dealing with that complexity?

Each time you increase the amount of code, your software grows exponentially more complicated.
– DHH, Getting Real

Once you add a feature, it's difficult to walk it back. From here on, you'll need a good sense of balance between maintaining existing implementations and adding new features.

Ask yourself: Would you prefer an app that's feature-rich but slow and unstable, or one that's simple yet lightweight, stable, and snappy?

Learn longevity secrets from long-standing companies called 'Shinise'

As Inkdrop's development surpassed 7 years, I experienced the deprecation of various libraries, and my thinking gradually became more conservative. However, I was unsure if this approach was really okay, so I decided to learn from predecessors who had tackled similar challenges.

By the way, about 90 percent of all businesses worldwide that are more than 100 years old are Japanese. Venerable shops and companies must have overcome the kinds of issues I'm facing now. So to sustain my solo development efforts long-term, I read the following Japanese book:

Gekkeikan (月桂冠) is a renowned Japanese sake brewery founded in 1637. While sake breweries may have an image of being rigid and conservative, that's not the case:

In the Meiji era, they introduced science and technology into sake brewing. At a time when barrel aging was prevalent, they developed non-preservative bottled sake, the first in Japan. They also pioneered a year-round, four-season brewing system. In recent years, they've operated a sake brewery in America to spread Japanese sake worldwide, combining steadfastness with innovation - a company embodying both aspects.

You can see their attitude of preserving tradition while continuously embracing technological innovation. Having survived over 300 years, they've lived through both the Industrial and Information Revolutions. Amazing.

Yamato Intec, a manufacturer of engine and automotive components, was founded in 1584.

After joining the company, all employees, including those hired for sales, must experience the casting manufacturing process for five years. This instills in everyone an unparalleled passion for casting and a family-like bond - something I heard repeatedly from many employees in interviews: "a passion for casting" and "a love for casting." It's proof that a dedication to casting is deeply rooted in the company culture.

While fearlessly taking on technological innovations, they never abandon their commitment to casting - this seems to be the secret to their longevity.

It is not the most intellectual of the species that survives; it is not the strongest that survives; but the species that survives is the one that is able best to adapt and adjust to the changing environment in which it finds itself
– Charles Darwin

In evolutionary theory, it's said that organisms capable of adapting to change survive.

Drawing from the wisdom of these predecessors, it seems crucial not to be stuck in conservatism while also boldly embracing technological innovation without losing sight of your core value.

More recently, generative AI has seen remarkable progress. However, change is still happening too quickly, with new developments sprouting up constantly. The emergence of new platforms like Apple Vision Pro is also noteworthy. We must maintain an appropriate distance from such nearby technological advancements while adapting accordingly.

Inkdrop's core value is its commitment to plain-text Markdown, offering an offline experience that's lightweight, snappy, and flexible for organizing notes. When adopting new technologies, I want to ensure they truly contribute to this core value. In terms of AI, I recently created video tutorials for the app, featuring a dog mascot as a companion generated through AI voice synthesis. These videos have received positive feedback, pioneering a new edutainment format that allows users to learn while being entertained.

You can't fully eliminate the element of luck

Despite everything written here, no matter how carefully you evaluate, failures will happen. So choose technologies with the assumption that failure is possible.

I'll end by quoting a favorite line from Attack on Titan, courtesy of Levi:

Image

Thank you for reading this far.

If you're looking for an app for technical notes, I'd be delighted if you check out Inkdrop.

Hope this article is helpful for your solo development journey!