Tech

How I Use Claude to Build Furrisi

A look at the skills, hooks, and CLAUDE.md habits I lean on while building my pet care platform with Claude Code


Working With Claude, Not Just Asking It

When I started building Furrisi, my pet care platform, I didn't want Claude to just answer one-off questions. I wanted it to work the way I work, to follow the patterns already in my codebase, catch mistakes before I do, and stay out of my way otherwise.

Getting there wasn't about writing better prompts. It was about setting up the environment around Claude: a few skills, a couple of hooks, and a deliberate habit around my CLAUDE.md file. Here's what actually stuck.

Teaching Claude My Patterns with a Skill

Furrisi has a lot of endpoints, and they all look similar. Same controller shape, same validation flow, same error handling, same way of talking to Prisma. Writing each new one by hand is repetitive, but explaining the pattern to Claude from scratch every time is just as tedious.

So I turned the pattern into a skill. The core of it is a single worked example: a reference controller that shows exactly how I want an endpoint written.

// Reference: how a Furrisi controller should look
const createPets: ExpressHandlers = async (req, res, next) => {
  // accept multiple pets
  try {
    const user = req.user!; // Guaranteed by authMiddleware
    const pets = req.body; //pets should be Pet[]

    if (!Array.isArray(pets) || pets.length <= 0) {
      throw new InvalidRequestError(
        "Request body must be a non-empty array of pets",
      );
    }

    await prisma.pet.createMany({
      data: ...,
    });

    const createdPets = await prisma.pet.findMany({
      ...
    });

    res.status(HTTPStatusCode.Created).json({
      success: true,
      data: createdPets,
    });
  } catch (error) {
    next(error);
  }
};

export { createPets };

Now, instead of describing all of that every time, I can just say "add an endpoint for recurring care schedules" and Claude writes it in the same shape — same validation, same ownership check, same status codes class, same error handling. The example does the teaching.

The nice part is that the skill captures intent, not just syntax. It's not "here's an Express handler," it's "here's how we write handlers in this project." That distinction is what makes the output feel like mine instead of generic boilerplate.

Keeping Code Clean with Hooks

Skills tell Claude how to write code. Hooks make sure the code is actually acceptable before it gets anywhere near me or production.

Hook 1: Lint after every code change

The first hook runs my linter automatically whenever Claude finishes writing or editing code. No matter how good the output is, formatting drift creeps in — a stray import order, inconsistent quotes, a missing trailing comma. Instead of reviewing style nitpicks by hand, I let the hook catch them.

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [{ "type": "command", "command": "npm run lint" }]
      }
    ]
  }
}

The effect is subtle but real: every diff I review is already formatted correctly. My attention goes to logic, not whitespace.

Hook 2: Run tests before production

The second hook is heavier and runs later, but before anything ships to production, it runs my unit and integration tests. Furrisi models a fair amount of relational data (pets, medications, care schedules), so a small change in one place can quietly break another.

This hook is my safety net. If Claude refactors a service or touches a shared query, the tests run and tell me immediately whether something downstream broke. I'd rather find out from a red test than from a user whose pet's medication schedule stopped syncing.

Together, the two hooks form a rhythm: lint continuously, test before it matters.

My Take on CLAUDE.md: Start Blank

The last piece is the one I'm most opinionated about.

A CLAUDE.md file is where you give Claude persistent context about your project. It's tempting to dump in the whole architecture, every convention, every gotcha, on day one.

I do the opposite. I start with a blank CLAUDE.md and add to it only when I actually need to.

My reasoning is simple: context has a cost. A bloated CLAUDE.md full of rules that rarely apply doesn't make Claude smarter, it makes it noisier. Every line competes for attention, and half of them are describing situations that never come up.

So I let the file grow from friction. When Claude gets something wrong in a way that a note would have prevented (e.g. misplaces a file, ignores a convention, reaches for the wrong library), that's my signal. I add one line to CLAUDE.md, and now it won't happen again. Everything in the file earned its place by solving a real problem.

The result is a CLAUDE.md that stays small, relevant, and easy to trust. It reads like a list of lessons learned, not a wall of speculative rules.

Final Thoughts

None of this is complicated. A skill that captures how I write endpoints. A hook to lint. A hook to test. A CLAUDE.md that earns every line. But together they turn Claude from a helpful assistant into something closer to a teammate that already knows the house rules.

The theme underneath all of it is the same: set up the environment once, so the day-to-day just works. I'd rather spend my energy building features for pets and their people than repeating myself to a tool.


← All posts