Why Titles and Price Tags Don't Guarantee Quality: The Work Speaks for Itself

작성자

카테고리:

← 피드로
DEV Community · Ecaterina Sevciuc · 2026-07-23 개발(SW)

How We Rebuilt a Website for a Musician and Why Engineering Ownership Matters More Than Commercial Labels.

In the tech market, people tend to judge a book by its cover: if a “design agency” takes on a project, clients are willing to pay big checks on blind trust. If an entry-level developer works on it, they’re met with skepticism (“they stand no chance of getting hired right now anyway”).

But do titles and price tags actually guarantee results? We recently completed a project for Canadian musician Richard (richardhigginsmusic.ca). And this story is the best answer to anyone who measures quality by brand name alone.

Background: The Client’s Journey Through “Professionals”

The story of this website is a classic example of a client going in circles searching for a decent solution:

  • The “Professional” Agency: Richard originally hired a specialized agency. They quoted a fee he assumed was the total cost, but halfway through, the agency demanded more money. In the end, the client was left with a half-baked mess despite paying a commercial rate.
  • DIY Attempts: Richard tried building the site himself using popular website builders, but ran straight into their rigid limitations.
  • Volunteer Format: Eventually, the project shifted into a free support/hands-on practice model. I stepped in alongside David (LinkedIn: David H. Watson, GitHub: @dEhiN — backend), taking full charge of the frontend, UI/UX, client logic, and site architecture.

For me, this wasn’t just a “learning pet project”—it was a real production case where I poured in over 320 hours of work, striving for top-tier quality.

The “BEFORE” Phase: What Did the Client Pay the Agency For?

When I received the raw source files from the studio (a WordPress dump, a bloated theme, and chaotic HTML/CSS), my initial reaction was deep repulsion. This wasn’t a site built by professionals; it was a product slapped together “on the knee” during coffee breaks.

What was under the hood and in the UI:

  • An Infrastructure Monster: A heavy system loaded with overlapping styles just to display a couple of audio tracks and photos.
  • Complete Disregard for Accessibility (a11y): A musician’s website should be comfortable for every single user. In the original codebase, accessibility logic was completely wrecked: broken element focus, lack of semantic structure, and a mess of CSS rules overriding native browser behavior.
  • Zero UX Consideration: Clunky player interactions, broken spacing, and complete unusable layouts on mobile devices.

Developer’s Reflection:
Everyone preaches Best Practices—semantics, accessibility (a11y), proper focus handling, clean code, and optimization. Then you look at code produced by a “commercial agency” and find a Frankenstein monster where all those standards were simply tossed aside.

When it comes to music, a website should be accessible and alive. If developers don’t care about that, it’s not a technology problem—it’s an attitude problem.

That’s exactly why I decided against patching up this WordPress corpse and chose to build a clean, lightweight, and accessible frontend from scratch—one I could actually be proud of.

The “AFTER” Phase: Having Nothing but 320 Hours and Engineering Passion

I can already hear the ironic smirks from seniors and team leads:
“320 hours on a single website?! Talk about inexperience, typical Junior. You could rewrite three SPAs in that time!”

Alright, let’s break down letter by letter what actually went into those 320 hours.

At the start, we had ABSOLUTELY NOTHING:

  • Design and Content? Zero. No logo, no promotional copy, not even a single usable photo. I spent hours generating promotional assets, tailoring them to fit various grid formats, layouts, and resolutions. Everything visual on this site is the work of a frontend developer wearing a designer hat.
  • Understanding What the Client Wanted? Nonexistent. The client (“since it was free”) expected a flawless outcome, but the vision kept shifting on the fly. I had to iterate through dozens of concepts and finalize a responsive layout.
  • Audio Content? There was only an idea: link the site’s playlist to Bandcamp so we wouldn’t have to hardcode every track card manually.

Technical Breakdown:

1. Audio Pipeline Automation (build-tools)

It turned out the client didn’t even have a set-up profile, and the audio files were in the wrong format. While David was uploading tracks to Bandcamp, I wrote a CLI script using Node.js and fluent-ffmpeg to automatically convert MP3s to required WAV formats, generate copies, and verify duration via ffprobe down to 60 ms tolerance, checking channel counts and sample rates along the way.

// MP3 to WAV conversion with auto-verification via ffprobe (excerpt)
async function convertAndVerify(inputPath, outGenerated, outDist, attempts = 1) {
  for (let attempt = 0; attempt <= attempts; attempt++) {
    try {
      await convertOne(inputPath, outGenerated, outDist);
      if (!VERIFY) return;

      const srcInfo = await ffprobeAsync(inputPath);
      const actualOut = SKIP_DIST ? outGenerated : outDist;
      const outInfo = await ffprobeAsync(actualOut);

      const durDiff = Math.abs((srcInfo.duration || 0) - (outInfo.duration || 0));
      const DURATION_THRESHOLD = 0.06; // 60ms толерантность

      const sampleMatch = srcInfo.sample_rate === outInfo.sample_rate;
      const channelsMatch = srcInfo.channels === outInfo.channels;

      if (durDiff > DURATION_THRESHOLD || !sampleMatch || !channelsMatch) {
        throw new Error(`Verification failed (durDiff: ${durDiff}s)`);
      }
      return;
    } catch (err) {
      if (attempt >= attempts) throw err;
    }
  }
}

Enter fullscreen mode Exit fullscreen mode

2. Dynamic Playlist Class (playlist.js)

Dedicated client-side logic was written for the music section:

– Backend Integration: The Playlist class executes requests with automatic retries (_fetchWithRetry) targeting /api/bandcamp.

– Inline Markup Cleanup (normalizeIframes): The method forcibly strips hardcoded width and height attributes from Bandcamp iframes, delegating layout control entirely to SCSS and CSS custom properties (--collapsed-height).

– Smart Expand/Collapse & Accessibility: Automatically calculates grid items per row (_computeItemsPerRow), dynamically updates aria-expanded and aria-controls attributes for the “See more / See less” button, and handles viewport changes with a debounce listener.

// Frame normalization and collapsed playlist height calculation (excerpt)
normalizeIframes() {
  const iframes = Array.from(document.querySelectorAll('iframe.playlist__iframe, iframe'));
  iframes.forEach((frame) => {
    try {
      frame.removeAttribute('width');
      frame.removeAttribute('height');
      if (frame.style) {
        frame.style.removeProperty('width');
        frame.style.removeProperty('height');
      }
    } catch (e) { /* ignore CORS/readonly */ }
  });
}

Enter fullscreen mode Exit fullscreen mode

3. Architecture: Multi-Page Feel Without Heavy Frameworks (Custom SPA Router)

The client preferred a traditional multi-page website (MPA) feel—with explicit URLs, intuitive navigation, and page state. However, I didn’t want to bloat the workspace, inflate bundle sizes, or deal with maintaining multi-page routing overhead.

The solution? Build a custom vanillajs SPA router using hash routing that looks like a traditional MPA from the outside while running blazingly fast under the hood.

The router intercepts internal link clicks, manages history.pushState / popstate, updates accessibility attributes (aria-current="page") and seamlessly handles browser back/forward buttons.

export default class Router {
  constructor(routes = {}, options = {}) {
    this.routes = routes;
    this.useHash = options.useHash !== undefined ? options.useHash : true;
    this._onLinkClick = this._onLinkClick.bind(this);
    this._onPopState = this._onPopState.bind(this);
  }

  init() {
    document.addEventListener('click', this._onLinkClick);
    window.addEventListener(this.useHash ? 'hashchange' : 'popstate', this._onPopState);
    const initialPath = this._getPath();
    this._routeTo(initialPath);
    try { history.replaceState({ path: initialPath }, '', window.location.href); } catch (e) {}
  }

  _onLinkClick(e) {
    const anchor = e.target.closest && e.target.closest('a');
    if (!anchor) return;
    const href = anchor.getAttribute('href');
    if (!href || href.startsWith('http') || anchor.target === '_blank') return;

    e.preventDefault();
    let path = href.startsWith('#') ? href.slice(1) || '/' : href;
    this.navigate(path);
  }

  _updateActiveLink(path) {
    const links = document.querySelectorAll('.header__nav-link');
    links.forEach((link) => {
      const linkPath = link.getAttribute('href').replace(/^#/, '');
      if (linkPath === path) link.setAttribute('aria-current', 'page');
      else link.removeAttribute('aria-current');
    });
  }
}

Enter fullscreen mode Exit fullscreen mode

4. What About Infrastructure and CI/CD?

Not to mention writing end-to-end (E2E) tests and configuring complete CI/CD deployment pipelines. At the start, we had no idea where or how we would host the project.

Ultimately, to save the client from ongoing hosting fees, we settled on Render.com’s free tier. You could say spending time configuring production-grade CI/CD was a bit overkill for this stage, but as a result, the project got a solid setup that deploys in a single click.

Granted, there were minor hiccups: post-deployment, we discovered Render’s free tier blocks outgoing email traffic (SMTP/NodeMailer) from the contact form. Live and learn! Now the client knows that proper email hosting requires a tiny upgrade, while the core web server runs like clockwork.

Conclusion: Quality Isn’t Measured by Price Tags

Is 320+ hours a lot? If you view a developer as a “button pusher executing pre-made Jira tasks and Figma designs”—yes, it is.

But if your job is to take absolute chaos, zero media assets, and broken legacy code, and turn it into a production-ready, beautiful, accessible (a11y), and optimized web product—that is the real cost of deep engineering commitment.

I’m proud of what we accomplished together on richardhigginsmusic.ca. The client got a fully functional platform, and we gained immense experience building clean architecture from the ground up.

So next time someone tells you entry-level developers can’t build anything without third-party libraries or constant senior oversight—just show them this case (The project is currently in the final handoff stage. Once all legal and technical details are finalized, I’ll happily share the repository/demo link!). In the end, the code and the work speak louder than any title.

원문에서 계속 ↗

코멘트

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다