LUKE FISHER

Ripple, a terminal RSS reader

6 Feb 2026

Since November I’ve been allocating a small amount of time each weekend to building a side project, Ripple. It’s a small RSS aggregator written in C. There are minimal dependencies and a pleasant text UI (TUI).

Motivation

I think every developer should do a project in C at some point. It’s great for appreciating the fancy features of modern languages such as Python or Go. Part of the motivation for this project was to really gain a deeper understanding of how memory works and what’s happening under the hood.

Another motivator was simply finishing a project. To build something more significant than a script thrown together in an afternoon, but also not something too over ambitious. Finishing or at least getting to a good initial version feels really good.

The final motivator for this project was to have the opportunity to design a system. Some projects don’t require much planning or thinking ahead of time. The path is super clear and it’s obvious what the next steps are. However, something like this requires more upfront thought. The project has a TUI, SQLite database, a custom RSS XML parser, and a minimal networking API. All of these components have to work together to be efficient, not leak memory, and provide a smooth user experience.

The repository can be found here.

Main page of the application

Features

The core features are listed below.

Future goals

What I would’ve done differently

I definitely would’ve come up with a more unified strategy for handling errors earlier. C can get pretty verbose if you handle every single possible error. I’ve made some assumptions about certain functions or routines never failing just because I didn’t want to deal with the error handling. C doesn’t have exceptions like Python or Java, so there isn’t a very graceful way to pass errors up the call stack and exit gracefully. Needless to say, there’s a LOT of poor error handling going on that needs to be refactored. I would probably start by unifying the types I return for success and failure. You could define a macro, something like RPL_OK and RPL_ERR, and then just return those everywhere. This is hard to do though when you have to deal with all kinds of error codes from SQLite and other libraries.

During the initial stages of development I assumed I would just use a text file to track all of the channels and articles. Well, inevitably once I got started implementing storing channels in a text file, I realized just how poorly it scales (related). I would have to come up with my own ways of sorting the channels, deleting channels would take forever (reading and re-writing the whole file), and storing anything complicated would be impossible. Next time, I’m just going to use a database from the start.

Earlier I talked about how important planning was for anything larger than a day-long project. Well, early on I hadn’t quite defined how big I wanted this to become, so I did only a small amount of planning. This led to me having to refactor functions to have different return values or take in different parameters, there was a lot of splitting functions and renaming things. Overall, much of that could’ve been avoided if I had already planned out the integration points between modules.

C is very easy to learn, but impossible to master. Everyone’s got an opinion on what constitutes good vs bad C. There are definitely ways to write obviously bad C, but for the most part what’s considered good is subjective and applies more on a case by case basis. I found myself taking too long to do simple tasks just because I wanted to do them the “idiomatic” way. Next time, I need to just move forward. Perfection doesn’t lead to done, and my goal is to finish projects. I spent way too long researching if using the goto statement for error handling and cleanup in a function is “good practice”. In reality, I should’ve just thought for a second, tried both, and picked the one I liked.

C strings can be a nightmare. They’re a trap for tough segment violation errors and buffer overruns. This combined with using a TUI led to some interesting bugs. There was one bug that only occurred when my window size was within a certain range. I was considering writing a dynamic string library to handle some of the tougher string processing in the RSS XML parsing module, however, this felt wrong as it would’ve required excessive use of the heap and calling malloc or calloc in some pretty tight loops. Then also I’m more likely to make mistakes leading to memory leaks. I opted to go for up front heap allocation and then operate on the strings directly. This of course leads to some brittle code since I’m mutating state. Admittedly, there was probably a better way to write the parser, but honestly, it’s held up well against some pretty malformed feeds. I’m happy with it for now, but if I had to start over, I’d probably do it differently.

Conclusion

I’m pretty satisfied with this project and there’s still more features that I’m excited to add. The best projects have a pretty simple core objective, but have room for fun additional features. Let me know if you find any bugs, I’m sure there’s plenty!