DEV Community

Cover image for 2025: I Shipped 3 OSS Projects — “This Was Actually Fine”
nyaomaru
nyaomaru

Posted on

2025: I Shipped 3 OSS Projects — “This Was Actually Fine”

Hi everyone!

I’m a frontend engineer, @nyaomaru.

Recently, for dieting purposes, I’ve been taking walks while carrying a backpack weighing over 10kg, kind of like Master Roshi’s (Kame-Sennin) training from Dragon Ball 🐢.

Another year flew by before I noticed it.

It’s already the end of the year.

At the end of the year, we have cleaning, shopping, and reflection.

In Japanese, December is called “Shiwasu(師走)”, meaning “even teachers run because they’re so busy.”

As for me, I’ve been running around so much that I feel like a completely worn-out rag.

Anyway, clean up this year’s mess within this year.

Let’s also organize our thoughts while we’re at it 🧹

So, I’d like to look back on 2025 from the perspective of OSS projects I released.

Just to be clear: there are no viral success stories here.


🎯 OSS Projects I Released in 2025

This year, I released the following three OSS projects:

  • is-kit

https://github.com/nyaomaru/is-kit

  • changelog-bot

https://github.com/nyaomaru/changelog-bot

  • divider

https://github.com/nyaomaru/divider

I continue maintaining and releasing updates for all of them.


🤔 Why Did I Build Them?

Simply put: because I wanted them myself.

  • is-kit: “There must be a cleaner way to write user-defined type guards…”
  • changelog-bot: “Writing CHANGELOG.md every time is honestly annoying…”
  • divider: “I just want to split strings more cleanly…”

Each project started from a small frustration.

I asked myself, “How can I remove this discomfort?” and started building.

Of course, it’s great when others use your OSS.
But my main focus was always: can this solve my own problem properly?

Let’s briefly look back at each project.


is-kit

If you use TypeScript, you probably write user-defined type guards fairly often.

For example:

type User = {
  id: string;
  age: number;
  role: 'admin' | 'guest' | 'trial';
};
Enter fullscreen mode Exit fullscreen mode

A typical type guard might look like this:

function isUser(value: unknown): value is User {
  if (typeof value !== 'object' || value === null) return false;

  const record = value as Record<string, unknown>;
  return (
    typeof record.id === 'string' &&
    typeof record.age === 'number' &&
    (record.role === 'admin' ||
      record.role === 'guest' ||
      record.role === 'trial')
  );
}
Enter fullscreen mode Exit fullscreen mode

I kept thinking: “Can’t this be simpler?”

That’s when the idea of LEGO blocks came to mind.
If we could compose small logical pieces, building complex type guards would be much easier.

So I designed is-kit around composable logic like and, or, and not.

With is-kit, the same example becomes:

import { struct, isString, isNumber, oneOfValues } from 'is-kit';

const isUser = struct<User>({
  id: isString,
  age: isNumber,
  role: oneOfValues('admin', 'guest', 'trial'),
});
Enter fullscreen mode Exit fullscreen mode

More declarative, more readable, and still type-safe — sounds nice, right?

You can also compose guards further:

import { and, narrowKeyTo, predicateToRefine } from 'is-kit';

type AdminUser = Readonly<User> & { role: 'admin' };

const byRole = narrowKeyTo(isUser, 'role');
const isAdminUser = byRole('admin');

const isAdultAdmin = and(
  isAdminUser,
  predicateToRefine((user: AdminUser) => user.age >= 18)
);
Enter fullscreen mode Exit fullscreen mode

What Worked Well with is-kit

A lot of people checked it out and gave it stars. Thank you so so so much 🙏🙏🙏

I also discovered tsd, a library for testing TypeScript type definitions, which was genuinely fun to work with.

The API stayed fairly simple, and overall, I’m happy with how it turned out.
There’s still room for improvement, so I’ll keep enhancing it.

Planned improvements include:

  • More primitive presets
  • More combinators
  • Improvements around struct

If you like it, feel free to give it a ⭐️!

https://github.com/nyaomaru/is-kit

The goal was never magic — just composability and readability.


changelog-bot

When you release an OSS project, you usually write release notes and update CHANGELOG.md.

But… it’s kind of a hassle, right?

At least, it was for me 🤮

There are tools that generate changelogs from conventional commits, but I wondered:

“Could AI classify changes based on content instead?”

That question led to changelog-bot.

You can use it via CLI with OpenAI or Anthropic API keys (AI is optional):

It’s mainly designed to run in CI.
Once a release is published, your CHANGELOG.md can be updated automatically.

name: Update Changelog

on:
  release:
    types: [published]

jobs:
  changelog:
    runs-on: ubuntu-latest
    permissions:
      contents: write
      pull-requests: write
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - uses: nyaomaru/changelog-bot@v0
        with:
          changelog-path: CHANGELOG.md
          base-branch: main
          provider: openai
          release-tag: ${{ github.event.release.tag_name }}
          release-name: ${{ github.event.release.tag_name }}
        env:
          OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
Enter fullscreen mode Exit fullscreen mode

You can also run it locally via CLI if you want. Details are in the README.

What Worked Well with changelog-bot

It completely removed the manual effort of maintaining CHANGELOG.md for my projects.
Releasing became much easier 🚀

I personally enjoyed designing the preprocessing logic that extracts and scores features before passing data to the AI.

That said, there’s still room for improvement, and contributions are welcome!

One note though:

Writing changelogs became easier. But... life itself didn’t magically become easier.

That part is still under observation.

https://github.com/nyaomaru/changelog-bot


divider

Sometimes you end up slicing strings over and over with substring, and it gets messy.

I wanted a cleaner way.

So I built divider, which lets you split strings in one shot using indices.

import { divider } from '@nyaomaru/divider';

const [a, b, c] = divider(text, 3, 6);
Enter fullscreen mode Exit fullscreen mode

What I Learned from divider

A great engineer contributed, I’m truly grateful 🙏

More importantly, I learned the basics of OSS hygiene:
CODE_OF_CONDUCT.md, CONTRIBUTING.md, CHANGELOG.md, DEVELOPER.md, etc.

However, there was a clear downside.

I couldn’t demonstrate a strong advantage over string.split().

That’s reflected in the number of stars, and honestly, it was a design mistake.

In short, this project was a “useful failure.”

Still, it was a valuable learning experience, and I’m glad I built it.

https://github.com/nyaomaru/divider


✨ Goals for 2026

In 2025, I:

  • Released OSS projects
  • Started writing technical articles
  • Moved to the Netherlands 🇳🇱

It was a year full of new challenges.

For 2026, I’m thinking about:

  • Releasing OSS applications related to DSA
  • Publishing a small game project

But above all, my main goal is simple:

Don’t burn out. Keep going. 🏃‍♂️

That applies to OSS, systems, and life itself.

Thank you for reading, and I hope you have a great year ahead.

See you in 2026 🐈

Top comments (0)