rhys/byjoio

Rhys · developer portfolio

Java backend engineer
in the making.
Web developer already.

Java for depth. The web for clients. Enterprise automation for a living. Everything here is real work with the decisions left in - including the ones I would make differently now.

Three ways in. Pick whichever matches why you came.

Days
RPA developer, Traverse Automation
Nights
BSc Computing & IT, Open University
Between
Freelance web development at byjoio

Project · Java

Mexveng

A 2D medieval RPG in Java and LibGDX. It exists because tutorials end before the hard parts begin - I wanted a codebase big enough that architecture decisions have consequences.

Top-down world, tile maps, entities, combat, dialogue. Solo project, long-running, and deliberately unglamorous under the hood: plain Java, explicit state, no magic. When something breaks at 11pm I want to read the code and know why.

Decision: why Java and LibGDX, not a full engine

I'm moving into Java backend work, so every hour in Mexveng is an hour in the language I'm betting my career on. LibGDX gives me a game loop and rendering, then stays out of the way - the architecture is mine to get right or wrong, which is exactly the practice I'm here for. Unity would have shipped a game faster and taught me less.

Decision: explicit over clever

Mexveng has a house rule: if a line needs a comment to explain what it does, rewrite the line. Cleverness is a cost you pay every time you read the file. The walkthrough below is real style, not cleaned up for visitors - four corner checks written out by hand instead of a loop, because four named checks read faster than one abstract one.

Feel the movement

This is not Mexveng running in your browser. It is Mexveng's movement and collision model, ported to about ninety lines of JavaScript so you can feel a design decision instead of reading about it. Notice how you slide along walls when moving diagonally into them - that behaviour falls out of the code below, not out of extra code.

Click or tab to the canvas, then WASD or arrow keys. Touch: use the pad.

The real thing, in Java

The class the demo above reimplements. Step through the decisions - each one exists because a bug taught me it should.

public final class Movement {

    private static final float MAX_STEP = 1f / 30f;

    private final TileMap map;
    private final Body body;

    public Movement(TileMap map, Body body) {
        this.map = map;
        this.body = body;
    }

    public void update(float rawDelta, Vector2 input) {
        float delta = Math.min(rawDelta, MAX_STEP);

        if (input.len2() > 1f) {
            input.nor();
        }

        float nextX = body.x + input.x * body.speed * delta;
        if (!collides(nextX, body.y)) {
            body.x = nextX;
        }

        float nextY = body.y + input.y * body.speed * delta;
        if (!collides(body.x, nextY)) {
            body.y = nextY;
        }
    }

    private boolean collides(float x, float y) {
        return map.isSolidAt(x, y)
            || map.isSolidAt(x + body.width, y)
            || map.isSolidAt(x, y + body.height)
            || map.isSolidAt(x + body.width, y + body.height);
    }
}
  1. Clamp the frame time

    Tab away from the game and come back: the next frame reports a huge delta, and an unclamped body teleports through walls. Clamping to a thirtieth of a second means the worst case is the game slowing down, never breaking. One line, one whole class of bugs gone.

  2. Normalise diagonal input

    Holding up and right gives an input vector of length 1.41 - free 41% speed boost for diagonal walkers. Normalising only when the length exceeds 1 keeps analogue sticks working: a half-tilted stick should still mean half speed.

  3. Move one axis at a time

    The decision the demo lets you feel. Try X and Y together and a diagonal walk into a wall just stops dead. Resolve X first, then Y, and sliding along walls falls out for free - no special case, no extra code. The best feature is the one the structure gives you.

  4. Four corners, written out

    This could be a loop over corner offsets. It is four explicit checks instead, because when collision misbehaves I want to see exactly which corner and why, in the file, at a glance. Explicit over clever - the house rule, applied.

Client work · byjoio

Static first

byjoio is my freelance practice. Small businesses come to me with slow, template-built sites. They leave with hand-built static ones - same content, a fraction of the weight, nothing to maintain.

The pattern is the same every time: semantic HTML, mobile-first CSS on custom properties, vanilla JavaScript only where the page earns it. GitHub Pages for hosting, Formspree for forms, analytics in the client's own account. At handover the client owns every piece - if I disappear tomorrow, their site doesn't.

Drag the handle

A composite of real rebuilds - client details removed, the shape kept. Left is what arrives; right is what leaves.

The template build

HomeAboutServicesGalleryBlogFAQContact
stock hero · 2.4 MB
stock
stock
stock
Subscribe to our newsletter! ×
We value your privacy · Accept all

The byjoio rebuild

Business name Call now
One line that says what they do,
and where they do it.
Real photos of real work. A phone number above the fold. Nothing else competing for the tap.
✓ What they do ✓ Where they work ✓ How to reach them
Typical template build
2-4 MB · 40+ requests · builder runtime, jQuery, three tracking scripts
Typical byjoio rebuild
Under 100 KB · under 10 requests · zero frameworks, zero build step
Decision: why static, when everyone sells a CMS

Most small-business sites are brochures, and brochures don't need servers. Static means nothing to patch, nothing to breach, hosting that costs nothing, and pages fast by construction rather than by optimisation. When a client genuinely needs to edit content weekly, that changes the answer - the point is to ask, not to assume.

Client names and links live with the clients, not on my portfolio. Ask me and I'll walk you through specific builds.

Client work · pattern

Forms without a backend

Every byjoio site needs a contact form. None of them need a server. This is the pattern that ships on all of them: native validation first, a honeypot for spam, Formspree for delivery, and a fallback that still works when JavaScript doesn't.

Try it

Live demo - nothing is sent anywhere.

There is a fourth field on this form that you cannot see. Spam bots fill it in; humans never do. That is the whole spam filter.

The code that ships

The actual submission handler from the byjoio pattern, step by step.

const form = document.querySelector('.contact-form');

form.addEventListener('submit', async (event) => {
  event.preventDefault();

  if (form.website.value !== '') {
    return;
  }

  if (!form.reportValidity()) {
    return;
  }

  const button = form.querySelector('[type=submit]');
  button.disabled = true;

  try {
    const response = await fetch(form.action, {
      method: 'POST',
      body: new FormData(form),
      headers: { Accept: 'application/json' },
    });
    if (!response.ok) {
      throw new Error(String(response.status));
    }
    form.hidden = true;
    document.querySelector('.form-sent').hidden = false;
  } catch {
    form.submit();
  }
});
  1. The honeypot

    The hidden "website" field. Bots auto-fill every input; humans can't see this one. If it has a value, drop the submission silently - no captcha, no friction for real visitors, and in practice it stops nearly all form spam on small sites.

  2. Let the browser validate

    required and type="email" in the markup already know the rules. reportValidity() shows the browser's own messages, in the visitor's own language, with correct focus handling. Reimplementing that in JavaScript is work I'd have to test and maintain for a worse result.

  3. Disable before sending

    A slow connection plus an impatient thumb means double submissions - and a client asking why enquiries arrive twice. Disabling the button before the request closes the gap.

  4. Fetch, with a real fallback

    Success swaps the form for a confirmation without leaving the page. But the catch block is the important line: if fetch fails for any reason, form.submit() falls back to a plain HTML post to Formspree's hosted page. The form works with JavaScript broken, blocked, or absent - the enhancement is optional, the function isn't.

Decision: why not just build a backend? I'm a backend developer.

Because the boring solution wins. A mail server I run is a mail server the client depends on me for, forever. Formspree in the client's own account costs them nothing at their volume, survives me, and turns a server-shaped problem into a paragraph of JavaScript. Knowing how to build it is exactly what lets me decide not to.

Deep dive · automation

The same process, twice

By day I automate business processes in Blue Prism at Traverse Automation. I'm moving toward building services in Java. The clearest way I can show you the difference is to solve one problem both ways.

The problem: 200 supplier invoices arrive by email every day. Each needs validating, coding, and entering into a finance system. Some days it's 12. Some days it's 900.

  1. 1 · The interface

    Blue Prism

    The bot drives the finance system's own screens - the same buttons and fields a human uses. No API required, and that is RPA's superpower: it automates systems nobody can change. Most enterprise software is exactly that.

    Java service

    The service needs a real seam: an API, a database, a file drop. If the finance system offers none, Java cannot start - which is why this problem landed on my desk as an RPA developer in the first place.

  2. 2 · Where the logic lives

    Blue Prism

    Process diagrams and object actions inside a proprietary tool. Readable to trained eyes, but diffing two versions of a process means eyeballing flowcharts. Review culture has to fight the tooling.

    Java service

    Plain text. Diffable, reviewable line by line, testable in isolation, refactorable with confidence. The entire ecosystem of software quality practice - version control, code review, CI - applies with no translation.

  3. 3 · When it breaks

    Blue Prism

    The vendor redesigns one screen and selectors break at 3am. You find out from the morning queue report. The mitigations are real engineering: work queues, retries, exception screenshots, alerting - built so failure is contained, not prevented.

    Java service

    Failures are exceptions you designed for, at boundaries you chose. The compiler removes a whole class of them before deploy; tests remove another. What remains fails loudly, in logs you structured, at the moment it happens.

  4. 4 · Change over time

    Blue Prism

    Fast to build - weeks, not quarters - and the business feels that speed. The cost moves to maintenance: every upstream UI change is your emergency. The bot is coupled to the one thing you don't control.

    Java service

    Slower to first value, because the cost is front-loaded into design. But coupling is to contracts, not screens. When the vendor redesigns their UI, nothing happens. The service ages like infrastructure, not like a workaround.

  5. 5 · The honest answer

    Blue Prism wins when

    The interface is human-only, the timeline is weeks, and the process might not exist in two years. That describes a huge amount of real business - RPA is the right call far more often than backend developers like to admit.

    Java wins when

    You own the systems, the volume matters, and the process must survive years of change. I want to spend my career on this kind of problem. The first kind is paying for the degree.

What the day job actually taught me

Production RPA is distributed-systems practice wearing a business-process costume. Idempotent steps, because bots get restarted mid-run. Work queues with retry limits and poison-item handling. Audit trails, because finance asks. Defensive waits at every boundary you don't control. None of that knowledge is Blue Prism knowledge - it moves with me to Java intact.

Skills

Tools

The levels are honest. Daily means I work with it every working day. Working means I ship with it. Building means I'm deliberately investing. Open a tool to see where it's used on this map.

Not listed: things I've only read about. If it's here, I've built with it.

Background

The long way to Java

Careers read cleaner backwards. This page starts at now and runs downhill into the past - scroll down to go back in time.

  1. Now

    Direction: Java backend engineering

    Not a whim - a convergence. The day job taught me how business systems fail. The degree is putting theory under the practice. Mexveng is where the language gets exercised until it's second nature. The rest of this site is the evidence.

  2. Ongoing

    BSc Computing & IT, Open University

    Part-time, alongside full-time work. Slower than the standard route and better for it: everything lands on problems I've already met in production, so the theory sticks.

  3. Ongoing

    byjoio - freelance web development

    Real clients, real deadlines, real handovers. Freelancing is where I learned that shipping is a skill separate from building - scoping honestly, saying no to features, leaving a client independent rather than dependent.

  4. Day job

    Traverse Automation - RPA developer

    Blue Prism in production: work queues, retries, audit trails, incidents at inconvenient hours. The job that taught me how businesses actually run - and exactly where the limits of automating-from-the-outside sit.

  5. 2016-2017

    Minecraft modding - first Java

    I wanted the game to do something it didn't, and the only path was code. First classes, first NullPointerException, first time changing a system from the inside. A decade later the tools are better and the reason hasn't changed.

That's the bottom. The only way from here is forward - back to now.

Current

Now

Three tracks, running at once, on purpose.

Days
RPA developer at Traverse Automation - Blue Prism automation in production, from design through support.
Nights
BSc Computing & IT at the Open University, part-time. Currently the long, worthwhile way to do a degree.
Between
byjoio client builds, and Mexveng - the Java RPG that keeps the language sharp while the career catches up to it.

Where this goes

A Java backend role, with people who review code properly. I bring production automation experience, freelance delivery discipline, and a codebase that proves I can sustain a system over time - not just start one.

Contact

No form, no funnel.

Email me and you get me.

Code lives at github.com/byjoio-dev, including this site.

The map