15 min read
Independent Development Diary 29: The Product Started Managing Me Back
I just wanted to make up a weekly diary today.
As a result, I looked through the project records and found that this was no longer a weekly diary, but more like a collection of accident reviews.
The last article was at the end of March, and now it’s mid-May. For more than a month, I felt like I was dealing with the "last problem" every day, and then I woke up the next day and discovered that yesterday's problem was just an appetizer.
This is the most annoying thing about making products.
The product doesn't end after the code is written.
It will start asking you for debts.
In the past, I always felt that the most difficult thing about independent development was writing a lot of code by myself. Looking at it now, it seems not.
The hardest thing is that someone actually uses what you write.
Once someone uses it, the question changes from "Can I make it?" to "Can I be responsible for it?"
This difference is quite big.
After all, the former is like clearing a game, and the latter is like when you suddenly find that NPCs in the game start sending you work orders.
StudyThai finally doesn’t look like a toy anymore
The changes in StudyThai during this time are obvious.
It’s not that the number of functions has increased, but the types of problems have changed.
In the past, most of the questions were: "Should this function be implemented?" "How to design this page?" "How to write this API?"
The questions now are: "Why didn't the user come back?" "Why is the payment status out of sync?" "Why is Android's permission blocked by Google?" "Why does the user say the audio is slow?" "Why does the same question in the feedback look like ten questions?"
To put it bluntly, products start to enter the real world.
The real world doesn't care how hard you write code.
It only cares: I ordered it, why not?
Cap Snap: There are pits behind one button
The most “new feature” thing in StudyThai recently is Cap Snap.
That is, photographing objects and identifying words.
When users see something and don’t know how to say it in Thai, they take a photo and the system recognizes the Thai words, IPA, and example sentences, and then saves it to a vocabulary book.
The idea itself is intuitive.
When you live in Thailand, if you see a certain fruit, a certain electrical appliance, or a certain roadside stall, you may not know what it is called in Chinese, let alone Thai. In traditional dictionaries, you first know the word and then look up the explanation. Cap Snap, in turn, is about seeing things first and then forming words.
The idea goes very well.
Implementation is not smooth.
At first I fantasized about being able to do more processing locally. Local identification on the phone, less money spent on the back end, and better privacy.
As a result, the real machine ran down, and the reality was very calm.
Fruits are okay, but when it comes to pots, bowls, razors, and reflective objects, the recognition starts to go crazy. The mobile terminal gives the backend several tags, and the backend relies on the tags to guess specific objects. The information has been squashed.
It's like asking someone to guess the name of a dish by listening to the sound through the door.
It would be strange to guess correctly.
The final plan became: cropping, direction correction, and background removal are done on the mobile phone, and the real recognition is left to the visual model.
The lesson this incident taught me is that technical solutions should not serve the purpose of "looking advanced".
Of course localization is good, but the accuracy after localization is bad, which means the cost is passed from the server to the user.
Users are not here to test your technical route.
Of course, this is what I thought from the beginning, so this paragraph is a self-criticism.
A paywall is not a page
Another main line of StudyThai is payment.
My previous understanding of paywall was too naive.
In the past, I thought it was just a pop-up window telling users to upgrade to Pro.
After working on the mobile version, I discovered that the paywall is not a page, but a payment link.
You have to manage Stripe, but you also have to manage the App Store and Google Play.
You have to manage RevenueCat, but also the subscription status in your own database.
You have to manage the promotional copywriting, as well as the price display in different regions.
You have to manage users’ click-to-purchase, and you also have to manage users’ click-to-restore purchases.
You need to control whether the payment is successful or whether the payment is successful but the backend has not yet been synchronized.
These words look very ordinary.
To deal with it, each one is more disgusting than the last.
For example, when restoring a purchase, if there is no prompt, the user will think that the button is broken.
For example, if the paywall is accessed from another full-screen modal, iOS may directly give you a look.
For example, after the Google Play account is migrated, RevenueCat's Service Account credentials will become invalid.
For example, if a promotion is hard-coded into the code, changing it is like defusing a bomb.
None of these things sound like "core functionality."
But if they break, users won't say "your non-core functionality is broken."
The user will just say: I can't pay.
At this time, you will find that collecting money itself is part of the product experience.
Maybe even the most expensive part.
Because there is a problem here, the loss is not "the user feels the experience is not good", but "the user wants to give you money, but you don't take it."
This is stupid.
The AI teacher has memory, but that doesn’t mean it will remember it.
During this time, I also did StudyThai Memory.
Simply put, it allows the AI teacher to read the user’s learning profile, such as which words have been learned, which words are about to be forgotten, and which words are poorly mastered.
In this way, when it answers, it will not be a general AI teacher, but can be tailored to your learning status.
I'm quite optimistic about this direction.
But after finishing the first version, I was quickly educated by reality.
Just because you give tools to a model doesn't mean the model will use them.
This is similar to a real company.
You open Notion, Jira, Google Drive, and Slack for your colleagues, and then they still ask you where the files are.
There is a huge gap between the existence of tools and the establishment of processes.
So I added a bunch of things that don’t look so sexy: when should I check learning files and when should I not check them; Pro users can only use it, free users can’t use it; Redis caching should not slow down the reply; whether the tool is actually called, there should be logs and burying points; if there is a problem, it should be able to downgrade, instead of directly blowing up the chat.
To put it bluntly, agentic products are not just about stuffing tools into them.
You also have to teach it when to use it, how to use it, what to do if it breaks out, and how to know if it is useful.
I don’t want to talk too much about this feature now.
It's still experimental.
Sometimes I am like a teacher who carefully prepares lessons, and sometimes I am like a teaching assistant who gets the wrong lesson plan.
But at least it's in the right direction.
The biggest problem with AI teachers in the past was that it didn’t know who you were.
Now it's finally starting to get a chance to know a little bit.
User feedback is more complicated than I thought
At the beginning of May, I reviewed all 80 open issues on GitHub.
After doing this, my biggest feeling is: user feedback is not a bug list.
It's more like a bunch of skeins mixed together.
Some data are wrong.
Some of the questions are not clearly designed.
Some of them are misunderstood by users themselves.
Some are the high-standard preferences of a certain power user.
In some cases, the feedback system itself records the wrong object.
Others are feature suggestions, not questions.
In the past, when I saw issues, I would easily get nervous subconsciously.
"It's over, something went wrong again."
It will be a little calmer now.
Of course, issues should be taken seriously, but every feedback cannot be treated as a vote for all users.
One user raised 18 items alone, accounting for 22.5% of all open issues.
This is not a bad thing.
People who are willing to write you feedback are much more valuable than most users who leave silently.
But you can’t directly regard the pain points of one highly sensitive user as the pain points of everyone.
Otherwise, the product will be led by a few loudest voices.
To put it bluntly, feedback is not the answer.
Feedback is just a clue.
You have to go back to the data, paths and real scenarios to make judgments.
This is why I feel like StudyThai is starting to “look like a product” recently.
Not because the functions are more complex, but because I want to start doing governance.
Data governance, feedback governance, content quality, transcription accuracy, payment path, and retention path.
None of it sounds like indie development.
Sounds like work.
What's even worse is that this class is still for himself.
The retention issue woke me up
During this period, we also conducted a paid conversion audit.
My suspicion at the beginning was whether users liked to start trials and then cancel, or whether the free benefits were given too much.
This suspicion is normal.
After all, when income does not meet expectations, people will instinctively wonder if they are too kind.
As a result, looking at the data, the direction is basically wrong.
The real problem isn't user testing.
Many users have left before they even get to the point where they actually need to pay.
This is embarrassing.
You carefully designed the paywall, trials, promotions, and price anchors at the back, but the user turned around and left after the first few steps.
It's a bit like you prepared champagne next to the finish line, but the runners bought a cup of milk tea next to the starting line and went home.
The completion rate of a single section of the course is actually pretty good, which shows that the individual learning experience is not completely unsatisfactory.
The problem is more like a lack of something to bring people back.
Streaks, pushes, daily goals, recall reminders, these sound Duolingo and annoying, but they are indeed part of the learning product.
I used to be a little resistant to this stuff.
I always feel that just making good content is enough.
Now I find that good content is only the foundation.
Learning is inherently anti-human.
You can’t expect users to open an app every day out of love.
I can't do it myself, so why should I ask users to do it?
OpenOwl: Terminal app punishes every optimistic person
OpenOwl is also continuing to push forward.
The last few versions haven’t really had any “cool-sounding” AI features.
Mainly studying very basic things.
Drag and drop multiple files.
Scroll bar interaction.
The UI freezes when a large amount of terminal output is output.
The file editor status is lost after switching panels.
The Git panel also works secretly in a hidden state.
There is no confirmation when exiting the app.
Packaging, notarization, DMG, GitHub release.
These things are very broken to write.
But that's it for terminal applications.
You think you're making a developer tool, but you're actually dealing with "why something so basic breaks" every day.
Copy and paste will break.
Drag will break.
The focus will break.
The scroll bar will break.
Hidden views will continue to accept keyboard input.
A SwiftUI page embeds AppKit, then embeds terminal surface, and then connects libghostty. If any layer is bad, what users will see in the end is that there is something wrong with your App.
I used to see some minor problems with terminal applications, and I would wonder why the basics were not done well.
Now I understand.
It’s not that they don’t want to do well.
The "basics" of this field are themselves not basic.
OpenOwl recently made a major layout adjustment.
The central area will be fixed to the terminal in the future and will no longer be replaced by panels such as Files, Git, and Deploy.
The right side is changed to a very narrow vertical rail, which can be expanded when necessary.
An independent terminal area is added to the sidebar, which can open some free shells that are not bound to projects.
The core of this change is not to make the UI look good.
But I finally admitted that the center of OpenOwl must be the terminal.
Files, Git, and deployment should all revolve around the terminal, rather than competing for position with the terminal.
This is also the biggest product judgment made this month.
OpenOwl should not become an IDE stuffed with AI features.
It should first become a stable, lightweight, and convenient workbench.
Use AI CLI whatever you like.
I don’t need to create another AI portal that “understands you better”.
To be honest, the biggest problem with many tools now is not that there is not enough AI.
Before the floor was paved, we started hanging spaceships on the wall.
I also cut off a feature that I had blown away
OpenOwl has another change during this period, which is quite shocking.
I have done a local deployment function before.
The idea is to directly manage local services in OpenOwl, such as the dev server of a certain project, and click to start and stop.
Sounds reasonable.
It took a while to find out that it was unreasonable.
Because OpenOwl is terminal-first.
Since the terminal is in the middle, why should I build a local service management in the App?
You can write code and most likely know how to run pnpm dev.
Not only is this function unnecessary, it also creates a lot of complexity for the surrounding system.
For example, when exiting the app, should the user be reminded that there are still local services running?
For example, how to align service status with terminal status?
For example, is the deployment panel deployed locally or remotely?
If a feature is in the wrong direction, it won't just waste your development time.
It will also continue to block nearby functions.
Finally I deleted it.
After deleting it, the project looks much cleaner.
This incident also reminded me that the most common mistake for independent development is not to make fewer functions, but to make one more function that you don’t use.
After all, you are alone and there is no product manager to stop you.
At this time, you yourself are the most dangerous product manager.
Content production must also be productized
There is another thing that has been tossing around for a while, which is content-engine.
The starting point of this thing is simple.
I find it increasingly difficult to write my weekly newspaper.
It's not because you don't do anything, but because you do too much and forget it after a few days.
When it was time to write the article, I only had a vague impression: I have been very busy recently.
This sentence contains no information.
So I started to treat content production as a product.
RSS collects information, AI selects topics, researchers check information, writers write manuscripts, and then derive X tweets, Xiaohongshu notes, and finally push them to WeChat drafts.
There are also two modes: Sanvi and Rin.
Rin can write more informational comments.
For Sanvi mode you must refer to my writing_style_guide.md.
But here I also encountered a very typical problem.
A style guide is not a repeater.
If AI just learns my catchphrases, such as "actually", "then" and "to put it bluntly", and then changes the structure of the reference article, that is not called imitation style.
That's called low-profile cleaning.
What you really need to learn is rhythm, perspective, judgment and use of materials.
For example, start with a specific little thing.
Like admitting that you don’t know either.
For example, technical concepts need to be translated into human language.
For example, don’t package failure as growth.
These are the styles.
It's not a rearrangement of what I said before.
This is why I feel more and more now that the most difficult thing about AI writing is not “writing like a human being”.
But don't turn things that people have lived into templates.
Templates are easy.
Being real is hard.
One-man companies are starting to become real
After this round of review, I found that the term "one-person company" has become more and more specific.
Before it was like a vision.
One person uses AI to do many projects with high efficiency, which sounds a bit cool.
Now it's more like a to-do list.
Product planning.
User feedback.
Mobile version.
Payment system.
Subscription sync.
Data analysis.
Service monitoring.
Content production.
Customer service explained.
And in the middle of the night, I suddenly discovered that a certain configuration was wrong.
AI does allow me to do more.
But it doesn't make these things go away.
It just compressed things that originally required several people to be shared in front of me.
Sometimes I also wonder if I should find someone to do it with me.
But the very real problem is that many times, if I explain to people for a long time, I might as well feed the context to the agent and let it run a version first.
The idea doesn't sound very politically correct.
But this is how I feel right now.
It’s not that people are unimportant.
But collaboration itself has costs.
When AI reduces execution costs, human communication costs are magnified.
This may also be a problem that many one-person companies will encounter in the future.
It’s not about whether we can recruit people.
It’s about when is it really more cost-effective to hire people than to open a few more agents?
To be honest, I don't know the answer either.
I'm just being pushed forward by the product now.
at last
As I write this, I don’t feel like a weekly diary.
It’s more like a person who is chased by his own product and comes back to take a breather.
StudyThai made me realize that real users will reveal all your laziness.
OpenOwl made me realize that the basic experience is harder than the AI gimmicks.
content-engine made me realize that I can’t even be lazy about “writing myself”.
AI hasn’t turned me into an easy one-person company.
It just made me really start to feel like a company, being chased by all sorts of things to deliver.