Building Software That Matters: What I’m Learning From Open Source, AI, and Real-World Development
personal look at building meaningful software through open source, AI, local-first development, developer tools, and continuous learning.
Building Software That Matters: What I’m Learning From Open Source, AI, and Real-World Development
Software development is often presented as a race.
Learn the newest framework.
Try the newest AI model.
Build something in a weekend.
Collect more technologies.
Get more GitHub stars.
Publish more projects.
Move on to the next thing.
But after spending time actually building software, I have started to think about development differently.
The most interesting part of programming is not simply writing code.
It is turning an idea into something real.
It is taking a problem that exists in someone's daily life, breaking it into smaller problems, designing a solution, writing the code, testing it, fixing what breaks, improving the experience, documenting the project, and eventually sharing the result with other people.
That process is much more difficult than creating a small demo.
And that is exactly why I enjoy it.
I want to build software that is not only interesting to create, but also useful to understand, test, improve, reuse, and maintain.
My journey across open-source projects, web development, programming languages, automation, artificial intelligence, local-first software, and developer tooling has taught me something important:
A project does not become valuable because it is complicated. It becomes valuable when it solves a clear problem well.
Why I Keep Building Projects
One of the best ways to learn software development is to build.
Tutorials are useful.
Courses are useful.
Documentation is essential.
Books can teach you concepts that are difficult to discover on your own.
But eventually, you have to build something where nobody has given you the exact answer.
That is where programming becomes different.
Suppose you learn how arrays work.
You may understand the syntax.
You may understand indexing.
You may know how loops work.
But when you create a real application, suddenly you have other questions:
How should the data be structured?
What happens when the input is invalid?
What happens when a file does not exist?
What happens when a user clicks a button twice?
How should errors be displayed?
How should the application behave when the network disappears?
How do you test the code?
How do you organize the project so that another developer can understand it?
How do you upgrade dependencies without breaking everything?
Those questions are where real engineering begins.
This is one reason I continue experimenting with different kinds of projects.
A calculator teaches different lessons from a note-taking application.
A game teaches different lessons from a database application.
A developer tool teaches different lessons from a website.
An AI application introduces another entire layer of challenges.
Every project gives me another opportunity to understand software from a different angle.
The Difference Between a Demo and a Product
A demo can prove that an idea is technically possible.
A product has to survive reality.
That difference is enormous.
A simple demo might say:
"Look, the feature works."
A real application has to answer:
"What happens tomorrow?"
What happens when thousands of users interact with it?
What happens with malformed input?
What happens when the storage becomes large?
What happens when the device has limited resources?
What happens when the user makes a mistake?
What happens when the software is updated six months later?
What happens when the original developer is no longer the only person working on it?
These questions are not always exciting.
They do not produce flashy screenshots.
They rarely appear in a five-minute coding video.
But they are extremely important.
Good software is often defined by the problems that users never notice.
A user may never know that the application has graceful error handling.
They may never know that the developer carefully handled corrupted data.
They may never notice that the application was designed to work without a network connection.
They may never see the tests.
They may never read the architecture documentation.
But those decisions still affect their experience.
That is why I increasingly care about the engineering behind a project, not just the feature list.
Open Source Is More Than Publishing Code
Open source is sometimes misunderstood as simply putting a repository on GitHub.
But publishing code is only the beginning.
A useful open-source project needs more than source files.
It needs a clear README.
It needs setup instructions.
It needs understandable structure.
It needs meaningful commits.
It needs documentation.
It needs issue tracking.
It needs versioning.
It needs releases.
It needs examples.
And ideally, it needs a reason for someone else to care.
A project becomes much more valuable when another developer can discover it and quickly understand:
- What problem does this solve?
- Who is it for?
- How do I install it?
- How do I run it?
- How do I use it?
- What technologies does it use?
- How can I contribute?
- What is planned next?
That is the difference between a code dump and a real project.
Open source also creates a strange but valuable feedback loop.
Once your code is public, someone else can inspect it.
They can ask questions.
They can report bugs.
They can suggest improvements.
They can build on top of it.
They can discover design decisions you did not notice yourself.
Sometimes that feedback is uncomfortable.
But it is useful.
Software improves when it is exposed to real users and real developers.
What I Have Learned From Building Many Small Projects
There is a temptation to think every new project should be bigger than the last.
But I have learned that small projects can teach very deep lessons.
A focused application forces you to think about scope.
A utility teaches precision.
A text-processing tool teaches edge cases.
A game teaches state management.
A database-backed app teaches persistence.
A search tool teaches indexing and retrieval.
A CLI tool teaches developer experience.
An AI application teaches the difference between model capability and product reliability.
This is why I do not think every project needs to become a startup.
Sometimes a project exists because you wanted to learn a difficult concept.
Sometimes it exists because you encountered an annoying problem and wanted a better tool.
Sometimes it exists because you wanted to experiment with a new language.
Sometimes it exists because you wanted to understand how a system works internally.
And sometimes a small experiment unexpectedly becomes something useful.
That is one of the great things about building software.
You do not always know the destination when you write the first line of code.
Learning Multiple Programming Languages
Learning multiple programming languages can look unnecessary from the outside.
Someone might ask:
"Why learn another language when you already know one?"
For me, the answer is simple.
Different languages encourage different ways of thinking.
A language with strong static typing teaches you to think carefully about data and interfaces.
A scripting language can make experimentation extremely fast.
A systems language forces you to pay closer attention to performance, ownership, memory, and resource management.
A language designed around functional concepts can change how you think about transformations and state.
A language focused on application development can teach you different patterns for user interfaces and platform integration.
The goal is not to collect languages like trophies.
The goal is to become a better problem solver.
If learning another language makes you approach problems differently, then the learning has value even before you use that language professionally.
Why Rust Is Interesting to Me
Rust is especially interesting because it sits close to the boundary between performance and safety.
It encourages developers to reason carefully about ownership, borrowing, lifetimes, mutability, and resource management.
At first, these concepts can feel restrictive.
But that difficulty is also the lesson.
When a compiler forces you to think about how data moves through a program, you begin to understand software behavior at a deeper level.
The important lesson is not simply:
"I learned Rust."
It is:
"I learned to reason more carefully about the software I am writing."
That lesson carries over into other languages too.
Good programming is not about memorizing syntax.
It is about understanding what your program is actually doing.
AI Is Changing Development, But It Does Not Remove Engineering
Artificial intelligence has changed the way software can be developed.
Code assistants can generate functions.
Models can explain errors.
AI can help with documentation.
AI can summarize large codebases.
AI can generate test cases.
AI can help explore unfamiliar technologies.
AI can accelerate prototyping.
All of these capabilities are useful.
But there is a major distinction between generating code and engineering software.
A model can produce code that looks convincing.
That does not automatically mean the code is correct.
It can contain subtle bugs.
It can make assumptions that are not valid for the application.
It can introduce unnecessary dependencies.
It can misunderstand the architecture.
It can create security problems.
It can produce outdated APIs.
It can solve the wrong problem very efficiently.
Therefore, I believe the future developer workflow will not simply be:
"AI writes everything."
Instead, a much more interesting workflow is:
Human defines the problem → AI accelerates implementation → Human verifies the result → tests validate behavior → users provide feedback → the system improves.
The ability to evaluate generated code becomes just as important as the ability to generate it.
Local-First Software and Why It Matters
One area of software development that I find particularly interesting is local-first computing.
The basic idea is simple:
Software should be able to do as much as reasonably possible on the user's own device.
Instead of treating the cloud as the default location for every piece of data and every operation, the application can keep important information locally and synchronize or back up when appropriate.
This approach can improve several areas of software design.
Privacy
When information remains on a user's device, fewer external systems necessarily need access to it.
That can simplify some privacy concerns.
But local-first does not automatically mean private.
Developers still need to think carefully about backups, analytics, authentication, synchronization, encryption, logs, and third-party services.
Privacy is an engineering property that has to be designed intentionally.
Offline Reliability
A local-first application can continue to work in environments where internet access is slow or unavailable.
That matters more than many cloud-first products assume.
A user may be traveling.
They may be in a building with weak connectivity.
Their network may simply fail.
Good software should degrade gracefully.
Speed
Local processing can reduce network latency.
The application can sometimes respond immediately instead of waiting for a server.
This can make the interface feel significantly better.
Control
Users can have greater control over their own data.
That can be particularly important for notes, documents, personal records, creative work, development projects, and other information that people expect to access reliably.
Building AI on the Device
Local-first ideas become even more interesting when combined with artificial intelligence.
Instead of sending every request to a remote server, an application can potentially perform some AI tasks directly on the device.
That creates a different engineering model.
The developer has to care about:
Model size.
Memory consumption.
Battery usage.
Inference speed.
Device capabilities.
Quantization.
Storage requirements.
Hardware acceleration.
Privacy.
Fallback behavior.
Model updates.
These constraints can make the problem harder.
But they also make the application more interesting.
A powerful cloud model with unlimited infrastructure is not the only possible future.
Another future is a collection of smaller, efficient systems running directly on phones, laptops, tablets, and other devices.
The right architecture will depend on the task.
Some operations may work entirely offline.
Some may require a larger remote model.
Some may use a hybrid approach.
The important thing is to make the boundary understandable.
Users should not have to guess whether their data is processed locally or sent elsewhere.
Clear software design includes clear communication.
The Most Important Feature Is Sometimes Trust
A software application can have hundreds of features and still fail to earn trust.
Trust often comes from much smaller details.
The application does what it says.
It does not unexpectedly lose data.
Errors are handled clearly.
Privacy choices are understandable.
Updates do not randomly break existing workflows.
The interface does not hide important behavior.
Documentation is accurate.
The project is transparent about limitations.
The source code is understandable.
These are not glamorous features.
But trust is cumulative.
Every good experience adds to it.
Every unexplained failure removes some of it.
When I think about the software I want to build, I increasingly think about this idea:
Make the system understandable.
Understandable software is easier to debug.
Understandable software is easier to maintain.
Understandable software is easier to contribute to.
Understandable software is easier for users to trust.
Why Documentation Matters So Much
Many developers treat documentation as something to write after the project is finished.
I see documentation differently.
Documentation is part of the product.
Imagine discovering an interesting repository but finding:
No README.
No installation instructions.
No screenshots.
No examples.
No explanation of why the project exists.
No information about the current release.
The code might be excellent.
But the barrier to entry is high.
Now imagine the opposite.
You open the repository and immediately understand the purpose.
You see how to install it.
You see a working example.
You understand the architecture.
You know what is planned.
You can run it within minutes.
That project feels welcoming.
This is especially important for open-source software because the first user is often another developer.
Documentation is therefore part of developer experience.
Projects Should Tell a Story
A repository is not only a collection of files.
It is also a story.
There is the original problem.
Then the first experiment.
Then the first version.
Then the bugs.
Then the redesign.
Then the improvements.
Then the release.
Then the feedback.
Then the next version.
Version control systems preserve part of this history.
A useful commit history can make the project easier to understand.
Instead of:
update
fix
changes
more changes
meaningful commits can communicate intent.
For example:
Add offline cache for recent notes
Handle invalid configuration values
Improve search indexing for large datasets
Add integration tests for synchronization
These messages create a technical narrative.
Someone exploring the repository can learn not only what the code looks like today, but how it evolved.
That is valuable.
Why I Like Developer Tools
Developer tools are especially interesting because developers themselves become the users.
That means usability matters at a different level.
A tool can be technically sophisticated but still frustrating to use.
A command-line utility might have powerful internals, but poor argument handling.
A code analysis tool might produce valuable results, but present them in a confusing format.
A project intelligence tool might understand a repository deeply, but make the results difficult to navigate.
The lesson is universal:
Technology is only part of the product.
The interface between the system and the user matters just as much.
That interface may be a graphical UI.
It may be a command-line interface.
It may be an API.
It may be a configuration file.
It may be a generated report.
Every one of these is part of the user experience.
Building Project Intelligence
As repositories become larger, understanding a codebase can become difficult.
There may be thousands of files.
Multiple languages.
Different build systems.
Dependencies with different versions.
Historical architectural changes.
Deprecated components.
Generated files.
Tests.
Documentation.
Scripts.
Configuration.
Infrastructure.
A developer entering such a repository needs answers.
What is this project?
What are the major components?
How do they communicate?
Which files are important?
How has the architecture evolved?
Where is complexity concentrated?
Which dependencies matter?
Which areas changed most frequently?
This is where repository intelligence becomes interesting.
Instead of treating GitHub repositories as collections of files, we can think of them as evolving systems.
The repository has a kind of identity.
Its architecture changes.
Its dependencies change.
Its complexity changes.
Its development patterns change.
Its history contains useful information.
Tools that expose this information can reduce the time developers spend performing codebase archaeology manually.
That idea is one of the reasons I find projects such as RepoDNA interesting.
The goal is not merely to display more code.
The goal is to help developers understand the system behind the code.
The Hidden Work Behind Every "Simple" App
A user sees a button.
A developer sees a chain of systems.
Consider a simple note-taking application.
The user thinks:
"I typed a note and pressed Save."
But the software may need to:
Validate the input.
Update the UI.
Serialize the content.
Write it to storage.
Handle storage errors.
Update search indexes.
Record timestamps.
Maintain ordering.
Trigger synchronization.
Update metadata.
Refresh the UI.
Handle crashes.
Protect data integrity.
Now imagine doing that for every feature in a larger application.
This is why software engineering becomes difficult at scale.
The complexity is not always visible.
It lives underneath the interface.
My Approach to Building Projects
I am increasingly interested in a development process that looks like this:
1. Start With the Problem
Before asking:
"What technology should I use?"
I try to ask:
"What problem am I solving?"
That question keeps the project grounded.
2. Define a Small First Version
The first version does not need to contain everything.
It needs to prove that the core idea works.
A small working system teaches more than a huge unfinished architecture.
3. Build the Core
The most important behavior should work reliably before secondary features are added.
4. Test the Edges
Normal input is easy.
The difficult cases are usually:
Empty input.
Huge input.
Invalid input.
Missing files.
Unexpected state.
Interrupted operations.
Repeated actions.
Network failures.
Storage failures.
Unsupported configurations.
5. Improve the Interface
Once the underlying behavior works, the experience needs attention.
Clear messages.
Useful defaults.
Good navigation.
Readable output.
Helpful errors.
6. Document It
Explain the purpose.
Explain installation.
Explain usage.
Explain architecture.
Explain limitations.
Explain future plans.
7. Release It
A release gives the project a meaningful checkpoint.
It turns "something I built" into "something people can try."
8. Listen
A project becomes more valuable when feedback changes it.
Why Finishing Matters
Developers often have many ideas.
I certainly do.
A new idea can feel more exciting than an unfinished project.
But there is a lesson in finishing.
Finishing teaches you what the middle of a project feels like.
Finishing teaches you how to debug old code.
Finishing teaches you how to clean up technical debt.
Finishing teaches you how to write documentation.
Finishing teaches you how to prepare releases.
Finishing teaches you how to handle the boring parts.
And the boring parts are often where professional engineering lives.
Starting is exciting.
Finishing is educational.
The Reality of Bugs
No serious software project is completely free of bugs.
That does not mean quality is unimportant.
It means software quality is a process.
Developers need mechanisms that catch failures.
Tests.
Logging.
Validation.
Monitoring.
Assertions.
Static analysis.
Code review.
User feedback.
Error reporting.
Automated builds.
Good architecture.
A good development process does not assume:
"There will be no bugs."
It assumes:
"Bugs will happen, and the system should help us find and fix them."
That mindset changes everything.
Security Should Be Part of Design
Security should not be a final checkbox.
It should influence architecture from the beginning.
Developers should ask:
What data does the application store?
Who should be able to access it?
What permissions are required?
What happens when credentials are compromised?
Can sensitive information leak through logs?
Are external services trusted?
What happens if input is malicious?
Are dependencies maintained?
Are secrets stored safely?
What happens if the user loses their device?
There is no universal answer to every security question.
But asking the questions early is valuable.
Security is not only about adding encryption at the end.
It is about reducing unnecessary exposure throughout the system.
The Importance of Performance
Performance is another area where engineering decisions matter.
A feature may work perfectly on a powerful development machine but behave differently on a slower device.
An application might be fast with a small dataset but slow with a large one.
An algorithm that works for a thousand records may become impractical for millions.
This is why performance should be considered in context.
Not every application needs extreme optimization.
Premature optimization can create unnecessary complexity.
But developers should understand the costs of their decisions.
How much memory does this operation use?
How many times does this loop execute?
What happens when the input grows?
Are repeated computations necessary?
Can data be cached?
Can work happen asynchronously?
Can the workload be moved closer to the user?
The objective is not simply "make everything fast."
It is:
Understand where time and resources are being spent.
Open Source and Learning in Public
One of the most powerful aspects of public development is that the learning becomes visible.
When I publish a project, I am not claiming to have solved every problem.
I am showing what I currently understand.
That creates an opportunity.
Someone else can read it.
They can suggest improvements.
They can point out a flaw.
They can offer a different approach.
They can fork the project.
They can build something completely different from the same idea.
This makes software development feel less like a solitary activity.
Even a personal repository can become part of a larger ecosystem of ideas.
Building a Developer Identity
A developer profile is more than a list of technologies.
"Python, JavaScript, Java, Rust, SQL..."
That list says what tools you have encountered.
It does not necessarily tell people how you think.
A project portfolio can communicate much more.
It can show how you approach problems.
It can show how you structure software.
It can show whether you care about testing.
It can show whether you document your work.
It can show how you respond to feedback.
It can show whether you finish projects.
This is why I want my projects to speak for themselves.
A good portfolio is not just:
"Here are the languages I know."
It is:
"Here is what I built, why I built it, what I learned, and what I am improving."
What I Want to Build Next
My long-term interests sit at the intersection of several areas.
Software engineering.
Artificial intelligence.
Developer tools.
Local-first applications.
Search.
Operating systems.
Automation.
Mobile development.
Open source.
These fields are changing rapidly.
That makes the learning process exciting.
One year from now, the tools will likely look different.
Some libraries will disappear.
New architectures will become popular.
AI capabilities will improve.
Hardware will change.
Development workflows will evolve.
The fundamentals will still matter.
Algorithms.
Data structures.
Operating systems.
Networking.
Databases.
Security.
Architecture.
Testing.
Product thinking.
Human-computer interaction.
These concepts remain valuable because technologies change faster than engineering principles.
Technology Should Serve People
It is easy to become obsessed with technology itself.
A new framework arrives.
Everyone talks about it.
Developers start using it.
Then another framework appears.
The cycle repeats.
But the user usually does not care.
Users care that the application works.
They care that it is fast enough.
They care that it does not lose their data.
They care that they can understand it.
They care that it solves their problem.
That is why I try to keep returning to a simple principle:
Technology should serve the problem, not replace the problem.
The best technology choice is not always the newest one.
It is often the one that creates the right balance between maintainability, performance, reliability, developer experience, and user needs.
From Projects to Products
There is an interesting transition that happens when a project gets serious.
At first, you ask:
"Can I build this?"
Later, you ask:
"Can somebody else use this?"
Then:
"Can somebody else install this?"
Then:
"Can somebody understand this?"
Then:
"Can this survive real usage?"
Then:
"Can I maintain this long term?"
And eventually:
"Can this become a product?"
That progression is important.
A product is not just a project with a payment button.
It is a system designed around ongoing value.
There may be:
User onboarding.
Support.
Updates.
Documentation.
Pricing.
Licensing.
Privacy.
Analytics.
Bug reports.
Feedback.
Backups.
Infrastructure.
Distribution.
A project can be an excellent learning experience.
A product introduces a different set of responsibilities.
Understanding that distinction has changed how I think about software.
What Success Means to Me
Success in software development is often measured using numbers.
GitHub stars.
Downloads.
Users.
Revenue.
Followers.
Commits.
Those measurements can be useful.
But they do not capture everything.
Sometimes success means finally understanding a difficult concept.
Sometimes success means fixing a bug that was extremely difficult to reproduce.
Sometimes success means writing documentation that allows another developer to use your project.
Sometimes success means someone discovers your work and builds something from it.
Sometimes success means finishing something you almost abandoned.
Numbers matter.
But learning matters too.
And impact matters even when it is difficult to measure.
The Long Game
Software development rewards persistence.
There are days when everything works.
There are days when nothing works.
There are days when one bug consumes hours.
There are days when a new idea completely changes your architecture.
There are days when you learn something in fifteen minutes that you had been struggling with for weeks.
The important thing is to continue.
Not blindly.
Not by repeating the same mistake.
But by improving the process.
Read.
Build.
Break things.
Test.
Debug.
Document.
Release.
Listen.
Improve.
Repeat.
That cycle is the real journey.
Final Thoughts
I do not want to build software simply because building software looks impressive.
I want to build things that are useful.
Things that solve problems.
Things that teach me something.
Things that other developers can understand.
Things that can evolve.
Things that can be tested.
Things that can be improved.
Things that can survive beyond the first demo.
Artificial intelligence will continue changing software development.
New languages will appear.
New frameworks will become popular.
New platforms will emerge.
But the fundamentals will remain.
Find a real problem.
Understand it.
Design carefully.
Build deliberately.
Test aggressively.
Document clearly.
Listen to users.
Improve continuously.
Share what you learn.
That is the kind of developer I am trying to become.
Not someone who simply knows the most technologies.
Not someone who creates the most demos.
But someone who can take an idea, understand the problem behind it, build a reliable system, and keep improving it.
That is the kind of software worth building.
Explore My Work
I publish my development work, experiments, open-source projects, technical writing, and new ideas online.
GitHub:
https://github.com/sanskarIN
Personal Website:
https://sanskarin.github.io/
If you are interested in open source, AI, developer tools, local-first software, programming, or building practical applications, you can follow along as I continue experimenting, learning, and shipping.
A Note to Fellow Developers
You do not need to build the biggest project.
You do not need to know every framework.
You do not need to wait until you are an expert.
Start with a problem.
Build a small solution.
Make it work.
Then make it better.
And when you learn something useful, share it.
Someone else may be struggling with the exact problem you just solved.
That is one of the best things about software development:
what you learn today can become someone else's starting point tomorrow.
Built in Public
This website is part of my ongoing effort to document ideas, projects, experiments, lessons, and the realities of becoming a stronger software developer.
Some projects will succeed.
Some will change direction.
Some will remain experiments.
Some may eventually become products.
That is okay.
The goal is not perfection.
The goal is progress that compounds.
Build. Learn. Share. Improve. Repeat.