Introducing Disorganized
Disorganized will be released within just a few weeks, and I want to talk about why the world needed yet another note taking app, and reflect on lessons learned so far. Disorganized was written in ClojureDart, which I’ll get to at the end.
Why?
I wanted one app to track my workouts, recipes, todo’s, general notes, and shopping lists.
All existing apps only supported one or two of these use cases at a time, so I created Disorganized which supports all of them (and more).
In Disorganized, notes are a mix of text, tables, and lists. For example, a recipe might be a table with ingredients and instructions in text. This in itself is not new. The trick comes from the fork feature. To carry over sections to later notes, you can “fork” your old note to start with the same sections as the last one.
This forms the fundamental workflow of Disorganized: You have a handful of initial notes with the structure that makes sense for you, then carry over and modify the structure as you go.
Good decisions
A good decision I made for the app is to make it for myself, with the hope that there are others like me.
Designing the app with this mindset has simplified things massively. Whenever I’m not sure about a feature or what to do next, I can just go use the app and it usually doesn’t take more than ten minutes to know what what works and what doesn’t. It remains to be seen if other people actually like or care about the app, but there is at least one person that thinks the app is well designed which is better than zero, something I initially feared.
Disorganized is written in ClojureDart. This in itself was a good decision - it’s a joy to use. But it also led to another good decision, discussing design and implementation with the creators of ClojureDart, Tensegritics.
I had a working version of the app months ago that I was about to launch, but made the decision to talk to Tensegritics first and oh boy was that the right decision.
Bad decisions
Today, the app relies heavily on Firebase.
The combination of Firebase and ClojureDart is effective enough that I’ve managed to get a working product together, while taking care of two small children, with a full time job, and without any significant app & frontend development experience. This inexperience resulted in my first mistake: Not embracing Firebase (or alternatives) from the start.
For example, the Clojure ecosystem is filled with amazing databases that are probably better than Firestore db in every way except dev time. But dev time is hugely important if you just have 8 hours a week to work on your own thing.
Complex code = complex app
My second mistake is fighting to implement complicated code. Not only was it time consuming, it was also a red flag that I initially ignored. Complicated code is a result of complicated design, and with time all complicated code that I fought so hard to implement has been removed, because the design that caused the complexity had to be changed.
A particular pain point has been text input fields. Don’t put business logic in them. Just let them be text fields and do your logic elsewhere.
More on ClojureDart
Flutter is incredible, and the fact that we can reach it from Clojure doubly so.
I remember telling my fiancée that it would take a week of frustration to get over the initial hump of learning Flutter/ClojureDart, but in the end I felt comfortable and got real work done after just a day or two.
I use re-dash for state management which has been nice. I can’t comment too much because I haven’t properly explored the alternatives, and I feel like I’m butchering the library every time I use it anyway.
One of the main thing people talk about when they discuss ClojureDart is the lack of a REPL (it’s supposedly on the way). Someone said that they are happy with the hot reload 80% of the time and desperately miss the REPL the rest. I disagree - I’m happy with the hot reload 97% of the time.
LLM’s
Claude/chatgpt are horrible at writing ClojureDart so instead I talk with them as if I was using plain Dart & Flutter, and then port the code to ClojureDart, or in some cases simply drop it in a dart file and import it. It’s been fine, and having some extra friction to prevent lazy copy pasting is not necessarily a downside.
Of course, project-wide prompts become impossible since LLM’s don’t understand ClojureDart, but I haven’t missed it. The app is just ~5000 lines of code, so it’s not that much ground to cover when trying to figure out what’s going wrong. I’d rather have the current scenario than writing it in a language that results in 20 000 lines of code, but with the ability to send all of them to an LLM.
What do I miss?
The main feature I want from ClojureDart in the future is better runtime error messages, and a way to see which line in the ClojureDart code the error happened. It’s usually pretty easy to figure out what went wrong (hint: it’s near the place you just edited).
But the times it hasn’t been as obvious I’ve resorted to checking the plain dart files that the ClojureDart compiler outputs, because there you do get the exact line number that caused the error. Often that line, or lines next to it, contain information you can use to infer where in your ClojureDart code the error happened.
I also find that there is friction in how Flutter and Clojure deals with nil. In Clojure you can pass around nil willy nilly but Flutter is often not as forgiving. For example when creating a text field, as Clojure programmers we might expect a nil value to be acceptable input and result in an empty string, but Flutter will instead crash.
That’s a minor point and easy to fix though.
All in all, creating Disorganized has been wonderful. Getting to work on my own idea in Clojure has been fun and motivational.
Can’t wait until the proper launch - it’s so close now.