Sometimes the best solution isn’t the most elegant one. Sometimes it’s the one that works, ships quickly, and solves the problem at hand. This is the story of how I built a search widget for my blog using what purists might call “hacks” – and why that was exactly the right choice.

If you haven’t already I suggest you check out Starting Small with Elm: A Widget Approach for some context; that’s where this Elm widget stuff all begun.

The Problem Link to heading

I wanted to add search functionality to my Hugo-powered blog. The obvious solutions were:

  • Add a search service like Algolia
  • Implement full-text search with a database
  • Use a static site search generator

All of these would work, but they also meant additional complexity, dependencies, or costs for what is essentially a simple personal blog with maybe 50 posts. I don’t even need search, really, it just felt like something fun and potentially lightweight to add.

The “Hack”: Parsing RSS Feeds Link to heading

I noticed that Hugo automatically generates an RSS feed at /index.xml containing all my posts with titles and descriptions. What if I just… parsed that?

getXmlFeed : Cmd Msg
getXmlFeed =
    Http.get
        { url = "/index.xml"
        , expect = Http.expectString GotXmlFeed
        }

The transformation logic is beautifully simple:

transformFeed : String -> List Post
transformFeed rawFeed =
    rawFeed
        |> String.split "<item>\n"
        >> List.drop 1
        >> List.map String.trim
        >> List.map
            (\entry ->
                { raw = entry |> String.toLower >> String.replace " " ""
                , title = entry |> parseProp "title"
                , link = entry |> parseProp "link"
                }
            )

Yes, I’m parsing XML with string splitting. Yes, this would make any XML parser maintainer weep. But it works perfectly for my controlled use case.

Why This is Actually Good Engineering Link to heading

1. Zero Dependencies Link to heading

No external services, no additional build steps, no API keys to manage. The search data comes from content that’s already being generated.

2. Instant Setup Link to heading

The entire search functionality was implemented in a few lines of Elm code. No configuration, no indexing pipeline, no deployment complexity.

3. Performant Link to heading

The RSS feed is small, loads once, and provides instant client-side search. No network requests during search, which gives a true real-time experience.

4. Reliable Link to heading

Since the RSS feed is generated by Hugo from the same content, it’s always in sync. No stale search indexes.

5. Maintainable Link to heading

The code is simple enough to understand completely. When something breaks, debugging is trivial.

The Pragmatic Principle Link to heading

This search widget embodies a key principle: match your solution complexity to your problem complexity.

For my personal blog:

  • The content is controlled and structured
  • The volume is small and predictable
  • The risk of the “hack” failing is low
  • The cost of a proper solution is high relative to the benefit (and, probably not a lot of fun)

For a large-scale application with user-generated content, dynamic data, and high traffic, you’d absolutely want a proper search solution. But for my use case, the simple approach wins.

When to Choose Pragmatic Hacks Link to heading

Consider the pragmatic approach when:

  1. Low risk environment - Limited scope, controlled data
  2. Time constraints - Need to ship quickly – be careful with this one, though; tech debt adds up quickly!
  3. Simple requirements - Basic functionality needed
  4. High cost of “proper” solution - Complex setup for simple needs
  5. Easy to replace later - Can evolve when requirements grow

The Result Link to heading

The search widget works beautifully. It provides real-time search across post titles and descriptions, weighs results by match frequency (a bit naive, sure), and offers a clean, responsive interface. Users get instant results without any backend complexity.

Most importantly, it took an afternoon to implement instead of a week, and it was a lot of fun. (I do enough “serious” coding for money anyways, so “fun” is actually a core value for all things related to this blog.)

In fact let me elaborate on that off-hand parenthesised side note a bit: I actually believe that it’s quite healthy to give ourselves some leeway in controlled circumstances to try out all that stuff we’d never dare do on our enterprise apps with corporate looking over our shoulders. Kind of like a death metal guitarist playing some funk riffs when nobody is watching, just to do something “different”. There’s plenty of time to dive into all sorts of best practices. But, especially in this mechanical AI hype age, let’s not forget to have fun and try new stuff, even if it isn’t completely safe/sane.

Conclusion Link to heading

Good engineering isn’t about always using the most sophisticated tools or following every best practice religiously. It’s about choosing the right tool for the job, considering the full context of your constraints and requirements. And, in the long run, good engineering depends on good engineers – and those need to experiment and not always do stuff by the book.

Sometimes the best solution is the simplest one that works. Don’t let perfect be the enemy of good – or in this case, don’t let “proper” be the enemy of “perfectly adequate, and also quite fun”. And surprisingly, to this hack’s defence: when all is said and done it actually follows my favorite Software Architecture Idiom: “Good Design Is Easier to Change Than Bad Design” (tip 14 from The Pragmatic Programmer: Your Journey To Mastery). ¯\(ツ)

The search widget proves that a little pragmatism can go a long way. Sometimes the hack is the feature future.