Architecting a Solution to a Real Problem: Find a Remote Job

The Problem

I need a job. But let’s be honest—manually applying to each listing is a soul-draining task. I’ve been through this before, and chances are, I’ll face it again. So, it makes sense to invest time in making this process scalable. My soul dies a little bit everytime I do repetitive tasks, so I want to automate it and this way I also help keeping my skills sharp!

Now let's go into the details (where the devil always hides <_<).

The Challenges

Automating job applications isn’t as straightforward as it sounds. There are a few key challenges to address:

  • Job boards come in all shapes and sizes—some have single-page forms, others require login, some just want an email.
  • Not every job fits my background or interests.
  • A single version of my CV isn’t ideal for every role (e.g., Engineering Manager vs Backend Developer vs SRE).
  • Job descriptions often contain constraints like "Only candidates from X location" or "On-site only" that can disqualify me before I even start.

The (Draft) Solution

After analyzing the problem, testing a few proof-of-concepts, and sketching out some ideas, I came up with this initial idea:

Here’s a breakdown of the components and how they play together:

1. Discover Jobs from Job Boards

  • Crawl job boards and collect links to job postings.
  • Visit each job link and extract the full Job Description (JD).
  • Store all relevant data in a database: JD text, job link, timestamp, board name, and any other useful metadata.

2. Query an LLM with the Job Description

  • At this stage, we analyze the JD using a language model.
  • This could be a hosted API (cheaper for personal use) or a self-hosted LLM on GPU-based machines (more scalable if turned into a SaaS).
  • The LLM extracts useful attributes like required experience, location, role, stack, and constraints, which we then store in the database.

3. Filter Jobs

  • Use SQL queries to exclude jobs that don’t match my criteria (e.g., minimum experience, required tech stack, location).
  • This keeps the system from blindly applying to irrelevant roles.

4. Tailor the CV to Each Job

  • Use a CV template as a base.
  • Programmatically adapt the content to better align with each job—highlighting relevant skills, tweaking summaries, and emphasizing applicable past experiences.
  • This step makes the application feel more personalized without doing it by hand.

5. Apply to Jobs

  • Automate the actual application submission.
  • Depending on the platform (e.g., greenhouse.io, lever.co), different automation strategies are used.
  • If a board isn't supported yet, the job is skipped for now.

Each job will move through several states in the pipeline, such as: DISCOVERED → LLM_EXTENDED → FILTERED_OUT or TAILORED_CV → APPLIED (or APPLY_NOT_SUPPORTED if the job board is still not supported)

Final Thoughts

This isn't a one-size-fits-all solution—it's more like a foundation I can build on. Even if it starts simple, the idea is to architect something that can grow, improve, and maybe even help others facing the same challenge in the future.

I think that every single process mentioned here adds value to the actual full process of searching for jobs, so even if not all are implemented at once, each has value for its own so I can start simple with one of them and improving as I continue my search in parallel.

What do you think about this? Do you see any improvement or new challenges to the potential application?

Let's see how it goes next week! Happy weekend

Why LLMs Can’t Replace Domain Experts: A Software Engineer’s Hands-On Lesson in Geophysics

Today I woke up really early — before sunrise — and couldn’t fall back asleep. So I thought, “Why don’t I learn a bit of Geophysics by applying my knowledge in Computer Science?”

Don’t you all wake up like this?

Well, I started searching around to see what Geophysics-related jobs actually do, and I learned superficially that they try to find minerals, water, oil, or understand earthquakes — without digging everywhere.

Some of the typical steps involved are:

  • Drill narrow holes in the ground to place different types of sensors.
  • Inject signals (like electric current) into the ground to stimulate responses recorded by those sensors.
  • Clean and process the recorded data.
  • Create models to simulate what might be underground.
  • Compare the simulation results with actual sensor data.
  • Adjust the model to reduce the differences — a process called Inversion.
  • Interpret the final results.

Sound familiar? Kinda like a Machine Learning loop, right?

There are software libraries that help with these steps. One example is SimPEG, which is Open Source.

I wanted to understand the basics, so I started my journey with the help of an LLM to build a very simple application that simulates a basic geophysical model — just to get a feel for how this process works.

So I asked:

Let’s say I want to build a very simple SimPEG-style app, but in Java. Let’s start with just the basics to understand how it works — and make it actually solve a simple problem using SimPEG as guidance.

The LLM answered (simplified, as it's actually a long read):

âś… Phase 1: Simulate a Simple DC Resistivity Problem
🎯 Goal: Simulate voltage at electrodes on the surface after injecting current into the ground — in 1D or 2D — using Ohm’s law and a basic PDE.

📦 Key Components

  1. Mesh (Grid) – An array where each index has a conductivity value.
  2. PDE Solver – The math behind the app.
  3. Source – Where and how current is injected and withdrawn from the mesh.
  4. Receiver / Observation – What a voltmeter "reads" (useful for comparing to real-world data in the inversion step).

After coding around and asking several questions to understand better some of this stuff, I ended up plotting some results, which looked "good" when I injected electrical current at index 30 and withdrew it at index 70 (in a mesh/array of 100 cells):

Then I experimented further. I moved the injection point to index 1, and suddenly the plot showed something I didn’t quite understand:

Why is the potential near the injection point so small in magnitude compared to the previous experiment? And why is the value at the withdrawal point so much larger?

So I asked the LLM to explain this asymmetry, and here’s what it said:

After reading that, I realized: I have absolutely no idea what it’s talking about.

I’m not a physicist. I did enjoy science classes in high school and even got decent grades, but eventually I specialized in Computer Science.

And that’s okay.

This experience reminded me why LLMs and AI won’t replace domain experts anytime soon. But it also highlighted how powerful these tools can be for running fast experiments outside your comfort zone — in my case, stepping beyond software engineering into geophysics.

What do you think of this? Is my experiment app throwing random numbers? Be welcomed to review this experiment if you know about the subject in my GitHub repository here.

Blogging My Way Through HTMX and Spring Boot

So here I am, after working all weekend to get this blog project up and running — all in the hopes of achieving a few things:

  • Practice some of my rusty skills in the Java ecosystem — file uploads, REST, OAuth2, Spring Security, Flyway, Docker, and more.
  • Have some fun with CSS animations (did you see that title when the page loaded?).
  • Build a blog using Markdown to write and display posts in a cooler way.
  • Try to understand how HTMX works

But I have to confess something: LLMs did most of the heavy lifting here — and I still don’t exactly understand how HTMX works. But hey, it works!

  • Also, this gave me a break from trying to finish the Outer Wilds DLC — I'm stuck! (Credits goes to u/Strange_Lettuce)

Let’s get back to HTMX. In the GitHub repository where I’ve uploaded the code, you’ll find this snippet inside templates/writepost.html:

<form hx-post="/upload-image" hx-trigger="change" hx-target="#upload-result" enctype="multipart/form-data">
  <input type="file" name="image" accept="image/*" />
  <input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}" />
</form>

<div id="upload-result"></div>

I can only guess some of this works, based on inference and past web development experience:

  • hx-post is the POST method I created under FileUploadController.java, so it's fair to say it's executed when the form is submitted.
  • hx-trigger is some sort of mechanism that tells when to execute the POST method in this case or submit the form.
  • hx-target would be the element that will be updated?

The th:name and th:value attributes are from Thymeleaf — nothing to do with HTMX — so I’ll ignore them here.

Let's ask ChatGPT to see if my guesses are correct!

And voilĂ ! The explanation matched up pretty well with what I thought:

hx-post="/upload-image"

  • When this form is triggered, HTMX makes an HTTP POST request to the /upload-image URL.
  • It submits the form data (including the file) via AJAX, without a full page reload.

hx-trigger="change"

  • This tells HTMX when to fire the request.
  • Here, the event is change on the form element. Practically, when the user selects a file in the <input type="file">, the form detects a change * and triggers the POST automatically.

hx-target="#upload-result"

  • This tells HTMX where to put the response from the server.
  • The server response (usually HTML) will be inserted inside the element with id upload-result (your <div id="upload-result"></div>).
  • So, for example, if the server responds with <p>Upload successful!</p>, that content will replace whatever is inside the #upload-result div.

Now, we all know that this could be a lie or hallucination. So I'll let it up for you to research on these HTMX tags further.

But in the end this gives a very critical hint on the way things will and are already changing in the future:

You don’t need to know every detail to begin using new technology — but without a solid grasp of the fundamentals, we risk building on shaky ground.

Understanding the “why” empowers us to guide LLMs with purpose, and to create things that are secure, efficient, and built to last. Not only to "work"

And this last bold phrases where not written by me. I just wanted to give some smart and more concise conclusion to this blog post before I go to sleep!

So good night đź’¤