TILs - Fueling Curiosity, One Insight at a Time

At Codemancers, we believe every day is an opportunity to grow. This section is where our team shares bite-sized discoveries, technical breakthroughs and fascinating nuggets of wisdom we've stumbled upon in our work.

Published
Author
user-image
Adithya Hebbar
System Analyst
Changesets: version bumps that write their own changelog

Changesets is a tool for versioning and publishing npm packages. Instead of editing package.json versions by hand and writing the changelog from git log at release time, you write down what changed as part of the change itself.

Set it up:

Code

npx @changesets/cli init


You get a .changeset/ folder and a config file. Then whenever you do something worth releasing:

Code

npx changeset


It asks which packages changed, whether it's patch/minor/major, and for a one-line summary. Your answers land in a markdown file in .changeset/ with a randomly generated name like tidy-pugs-shake.md:

Code

---
"@myscope/core": minor
"@myscope/cli": patch
---

Add a `--dry-run` flag to the publish command


Frontmatter says which packages get bumped and by how much, the body is the changelog entry. It's a plain file, so you can also skip the prompt and write one yourself, or edit it later if the wording was bad. Commit it with the code. These files pile up as you work, one per change.

Release time:

Code

npx changeset version   # eats the markdown files, bumps versions, writes CHANGELOG.md
npx changeset publish   # publishes whatever isn't on npm yet, and tags it


changeset version is the useful bit in a monorepo. Bump @myscope/core a minor, and @myscope/cli that depends on it gets bumped too, with the internal dependency range updated.

#changesets #npm #monorepo #javascript
Published
Author
user-image
Adithya Hebbar
System Analyst
πŸš€ Verdaccio: A local npm registry
Verdaccio is an npm registry that runs on your laptop. You can publish to it and install packages from it just like you would with npm, and you get the same tarball npm would have served.
If Verdaccio doesn't have a package, it fetches it from the public npm registry and caches it, so the rest of your dependencies continue to install normally.

Start Verdaccio:

Code

npx verdaccio # listens on http://localhost:4873


Create a local user before publishing:

Code

npm adduser --registry http://localhost:4873


Publish your package:

Code

npm publish --registry http://localhost:4873


Install it somewhere else:

Code

npm install my-package --registry http://localhost:4873


There's also a web UI at http://localhost:4873 where you can see everything you've published.

Passing --registry on every command gets old. For scoped packages, you can add one line to the consuming project's .npmrc:

Code

@myscope:registry=http://localhost:4873


Now @myscope/* resolves from your local Verdaccio registry, while everything else continues to use npm.
Just remember to remove the line when you're done. Otherwise, you'll eventually wonder why npm install is failing on a machine where Verdaccio isn't running. πŸ˜„

#npm #nodejs #verdaccio #javascript
Published
Author
user-image
Satya
Expose a Kubernetes cluster workload to the public internet using Tailscale Funnel.
Our cluster is tailnet-only (nothing public), which breaks inbound webhooks , e.g. Slack Event Subscriptions can't reach a private host.
Funnel (via the Tailscale k8s operator) exposes a single endpoint publicly over HTTPS at https://<name>.<tailnet>.ts.net cert auto-provisioned, everything else stays private + outbound-only.
It's just an Ingress:

Code

annotations: { tailscale.com/funnel: "true" }
ingressClassName: tailscale


Useful for: Slack/GitHub/Stripe webhooks, OAuth callbacks, demo links , expose one path, no public LB, no firewall hole.
Gotcha: needs funnel enabled in the tailnet ACL.
Link: https://tailscale.com/docs/kubernetes-operator/ingress/expose-workload-to-internet
#tailscale #k8s #slack #infra
Published
Author
user-image
Satya
move apps in fly from one org to another using
fly apps move <app-name> --org <target-org>

Note: Moves app as well as the secrets automatically .

#fly
Published
Author
user-image
Syed
Dolt Workbench. Local UI for Beads Issues

Dolt Workbench is a SQL UI tool that connects to our local Dolt database. We can browse issues, run queries, and see commit history and more

How to connect:

1. Make sure your Dolt server is running:

sql

bd dolt start
  bd dolt status    # note the port number


2. Open Dolt Workbench and create a new connection:

sql

Connection Name: <name-here>
  Type: MySQL/Dolt
  URL: <mysql://[email protected]>:<port>/beads


3. Leave password empty. Click "Launch Workbench".

What we can do:
β€’ Browse tables: issues, dependencies, events, comments, config
β€’ Run SQL queries: same as bd sql but with a visual editor
β€’ View commit log: see every change made to the database
β€’ Edit rows: update issue fields directly from the spreadsheet UI
β€’ View diffs: compare changes between commits
#beads #dolt
Published
Author
user-image
Syed
Beads (bd) uses Dolt as its database backend. A SQL database with Git-like version control. Issues are stored locally in .beads/dolt/ and synced to GitHub via invisible refs (refs/dolt/data).

Setup (fresh clone)

Code

brew install beads
sudo bash -c 'curl -L https://github.com/dolthub/dolt/releases/latest/download/install.sh | bash'
mkdir -p .beads/dolt
dolt clone git+https://github.com/<org>/<repo>.git .beads/dolt/beads
bd dolt start
bd list


Daily commands

Code

bd ready                  # see available work
bd list                   # all issues
bd show <id>              # issue details
bd create --title="..." --type=task --priority=2  # create issue
bd update <id> --claim    # claim work
bd close <id>             # complete work


Sync with team

Code

bd dolt pull              # pull teammate's issues
bd dolt commit            # commit pending changes
bd dolt push              # push your issues


#dolt #beads
Published
Author
user-image
Syed
NDJSON (Newline Delimited JSON): A file format where each line is a valid, independent JSON object, separated by a newline character (\
)

Regular JSON wraps everything in an array:

Code

[
    {"name": "Alice"},
    {"name": "Bob"}
  ]


NDJSON is one object per line, no wrapper:

Code

{"name": "Alice"}
  {"name": "Bob"}


Didn't know this existed. Turns out it's really useful for:
- Log files β€” each event is one line, easy to grep
- Streaming APIs β€” send objects as they're ready, don't wait for the full array
- Large datasets β€” process line by line without loading everything into memory

Also called JSONL (JSON Lines). Same thing. Claude Code uses this to send MCP messages over stdio
#json
Published
Author
user-image
Nitturu
Ruby hashes treat strings and symbols as different keys.
But JSON data (JWT, APIs) always comes with string keys.

Ruby

decoded = { "user_id" => 1 }

decoded[:user_id]   # => nil ❌
decoded["user_id"]  # => 1


This silently breaks code when Rails-style symbol access is used.

We can use HashWithIndifferentAccess which removes this mismatch:

Ruby

payload = HashWithIndifferentAccess.new(decoded)

payload[:user_id]   # => 1
payload["user_id"]  # => 1


You no longer care whether keys are strings or symbols. Rails params works the same way internally.

HashWithIndifferentAccess is a class provided by ActiveSupport, so it exists only in Rails, not in core Ruby. It internally normalizes all keys (by storing them as strings) while allowing access using either symbols or strings. It’s designed specifically for boundary dataβ€”like JSON responses, JWT payloads, and request paramsβ€”where key formats are inconsistent. By removing the need to care about key types, it prevents subtle nil bugs without forcing changes in how the rest of the code is written, which is why Rails uses it internally for params

#Rails
Published
Author
user-image
Satya
When two models are associatedβ€”for example, a Chat that has many Messagesβ€”you can create a message using @chat.messages.build instead of instantiating Message.new. This automatically sets the chat_id on the message, so it’s never niland the association is correctly maintained

#ActiveRecord #Rails
Published
Author
user-image
Syed
What is Framing in WebSockets?
Framing is how WebSocket data is split, structured, and transmitted over the wire. Framing is protocol-level, not app-level

WebSockets do not send raw strings or JSON directly. Instead, every message is wrapped inside WebSocket frames.

Each WebSocket frame contains:
β€’ FIN bit – is this the final frame?
β€’ Opcode – what type of data?
β€’ Payload length
β€’ Masking key (client β†’ server)
β€’ Payload data
Frame Types
Data frames
β€’ Text frame β†’ UTF-8 text (JSON, strings)
β€’ Binary frame β†’ raw bytes (files, protobuf)
Control frames
β€’ PING β†’ check if connection is alive
β€’ PONG β†’ response to ping
β€’ CLOSE β†’ graceful shutdown
#websockets #client #server
Published
Author
user-image
Swasthik
Cron in GitHub Actions
In GitHub Actions, you can schedule workflows using cron syntax inside the on.schedule field. It uses UTC time format.
Example:

Code

on:
  schedule:
    # Runs at 5:30 AM and 5:30 PM UTC every day
    - cron: '30 5,17 * * *'
    # Runs every 15 minutes
    - cron: '*/15 * * * *'
    # Runs every Monday at 9:00 AM UTC
    - cron: '0 9 * * 1'


Cron format:
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€ minute (0–59)
β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€ hour (0–23)
β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€ day of month (1–31)
β”‚ β”‚ β”‚ β”Œβ”€β”€β”€ month (1–12)
β”‚ β”‚ β”‚ β”‚ β”Œβ”€ day of week (0–6, Sun=0)
β”‚ β”‚ β”‚ β”‚ β”‚
* * * * *

#GitHubActions #DevOps
Published
Author
user-image
Swasthik
Syntax Highlighting & Language Detection with Highlight.js

It's a lightweight JS library that automatically highlights and detects code syntax on web pages β€” no need for manual markup.

Installing Highlight.js

JavaScript

pnpm add highlight.js


Detect Language & Highlight

JavaScript

import hljs from 'highlight.js';
import 'highlight.js/styles/atom-one-dark.css';

const code = `function sum(a, b) { return a + b; }`;
const { value, language } = hljs.highlightAuto(code);

console.log(language); // e.g. "javascript"
console.log(value); // highlighted HTML


β€’ You can pass value to your React component and render it inside a code element with the hljs class for styling.
Highlight.js automatically detects the language and highlights it β€” perfect when your app handles multiple code types dynamically.

#JavaScript #Frontend #HighlightJS
Published
Author
user-image
Vaibhav Yadav
Senior System Analyst
Clean up merged Git branches easily

You can quickly delete all local branches that have already been merged into your current branch (like master or main) using a single command:


Code

git branch --merged | egrep -v "(^\\*|master|main)" | xargs git branch -d


This command lists all merged branches, filters out the current and main branches, and deletes the rest safely. Perfect for keeping your local repo tidy after merging multiple feature branches.

#git #github #vcs
Published
Author
user-image
Puneeth
Understanding the Difference Between Regular Imports and .forRoot() in NestJS

In NestJS, modules can be imported in two main ways β€” but they behave differently:

1 Regular Imports
β€’ Example imports: [ProjectsModule, UserModule]
β€’ These are simple β€” they bring in the module’s exports (like services or guards).
β€’ They don’t need any setup or configuration.
2.1 forRoot() Imports
β€’ Example imports: [SlackModule.forRoot(dbClient)
β€’ These are special. They let a module set itself up with configuration or dependencies (like a DB client, API key, etc.).
β€’ They create a global instance that’s initialized once across the app.
2.2 forRootAsync()
β€’ Used when setup depends on async config β€” like environment variables.
β€’ Example :

Code

PgBossModule.forRootAsync({
  useFactory: async (config) => ({
    connectionString: config.get('DATABASE_URL'),
  }),
  inject: [ConfigService],
});


Conclusion
β€’ Regular imports are sufficient when we just need access to a module’s services or exports.
β€’ forRoot() is useful when a module requires initial configuration or dependencies.
β€’ forRootAsync() is ideal when configuration depends on asynchronous operations, such as reading environment variables or fetching secrets.
#NestJS #import_module
Published
Author
user-image
Syed
getBoundingClientRect() A crucial DOM method that gives us an element's position and size relative to the viewport, not the document.

Example:

JavaScript

const rect = container.getBoundingClientRect();


What getBoundingClientRect() returns?

JavaScript

{
  top: 100,      // Distance from viewport top to element top
  left: 200,     // Distance from viewport left to element left  
  right: 400,    // Distance from viewport left to element right
  bottom: 300,   // Distance from viewport top to element bottom
  width: 200,    // Element width
  height: 200,   // Element height
  x: 200,        // Same as left (for compatibility)
  y: 100         // Same as top (for compatibility)
}


Real Usage Example: Image Comparison Slider:

JavaScript

// User drags the slider to compare two images
handleDrag(event) {
  const container = this.getContainer(); // The image container
  const rect = container.getBoundingClientRect();
  
  // Convert global mouse position to slider position
  const x = event.clientX - rect.left;  // Mouse X relative to container
  const percentage = this.calculatePercentage(x, rect.width);
  
  // Update slider position (0-100%)
  this.sliderPosition = percentage;
  this.updateSliderPosition(percentage);
}


Step-by-Step Breakdown:
1. User drags mouse β†’ event.clientX = 350 (global position)
2. Get container bounds β†’ rect.left = 200 (container starts at 200px from viewport left)
3. Calculate relative position β†’ 350 - 200 = 150px (mouse is 150px from container's left edge)
4. Convert to percentage β†’ 150 / 400 = 37.5% (150px out of 400px container width)
5. Update slider β†’ Move slider to 37.5% position
getBoundingClientRect() is the bridge between global coordinates and element-relative coordinates.

#CCT1JMA0Z #stimulus
Published
Author
user-image
Syed
When we rewrite git history (like squashing commits, interactive rebase), our local branch diverges from the remote branch.
A regular git push will be rejected because the histories don't match. The first option is git push --force, which overwrites the remote branch with our local version.

The Usual Approach: git push --force

Code

# DANGEROUS - can overwrite other people's work
# We do it everyday though
git push --force origin <feature-branch>


The Safer Alternative: git push --force-with-lease

Code

# SAFER - includes safety checks
git push --force-with-lease origin feature-branch


How --force-with-lease works:
β€’ Checks if the remote branch has moved since we last fetched it.
β€’ Only overwrites if the remote is exactly where we expect it to be.
β€’ Fails safely if someone else has pushed commits we don't have locally.
β€’ Prevents accidental overwrites of other people's work.
PS: Might sound simple, but helps a lot when working with bigger teams.

#git #github
Published
Author
user-image
Nived
If a port (say 3000) is already in use, you can check what’s running on it with:

Code

lsof -i :3000


Here, lsof stands for β€œList Open Files” β€” and in Unix/Linux, everything is a file, including network sockets.

So this command lists all processes that have files (or ports) open.
Example output:

Code

COMMAND   PID  USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
node     1234  nived  22u  IPv6  0x...      0t0  TCP *:3000 (LISTEN)


Now you can see the PID (Process ID) of the program using the port.

To stop it, run:

Code

kill 1234


The kill command sends a signal to a process β€” by default, it’s SIGTERM (Signal Terminate), which politely asks the process to shut down.
If the process refuses to die, you can forcefully stop it with:

Code

kill -9 1234


Here, -9 represents the SIGKILL (Signal Kill) β€” which immediately ends the process without cleanup.

#unix
Published
Author
user-image
Mohammad
The JavaScript console isn’t just console.log. It provides specialized methods for different scenarios:
β€’ console.error() β†’ logs errors in red.
β€’ console.warn() β†’ highlights warnings.
β€’ console.table() β†’ formats arrays or objects as tables.
β€’ console.group() / console.groupEnd() β†’ groups related logs for better organization.
πŸ’‘ Using these methods makes debugging clearer and more structured than relying only on console.log.
#Javascript
Published
Author
user-image
Syed
How to deploy to fly.

1. Initial Setup

Code

# Initialize the app

fly launch

# This creates:
# - fly.toml (basic configuration)
# - .dockerignore
# - Dockerfile


2. App Creation

Code

# Create app in specific organization

fly apps create ui-delta-c9s --org c9s-staging


3. Configuration Setup

Code

# Set required secrets
# Example

fly secrets set RAILS_MASTER_KEY=$(cat config/master.key) --app ui-delta-c9s
fly secrets set SECRET_KEY_BASE=$(openssl rand -hex 64) --app ui-delta-c9s
fly secrets set RAILS_ENV=production --app ui-delta-c9s


4. Database Setup

Code

# Option 1: Create new database
fly postgres create --name ui-delta-db --region sin

# Option 2: Attach to shared cluster
fly postgres attach postgres-fire-9606 -a ui-delta-c9s


5. Customize fly.toml as required for the project

6. Deploy

Code

fly deploy --app ui-delta-c9s


#fly #deploy
Published
Author
user-image
Syed
The Problem:
https://Fly.io|Fly.io databases use internal networks (.flycast domains) that aren't publicly accessible, so we can't connect directly from external tools like DBeaver.

Solution:
Use https://Fly.io|Fly.io's proxy to create a secure tunnel:

Code

fly proxy 5433:5432 --app <cluster_name>


How It Works:
β€’ Local port 5433 β†’ Remote database port 5432
β€’ Creates a secure tunnel through https://Fly.io|Fly.io's network
β€’ Keep the proxy running while using external tools
β€’ Allows external tools like DBeaver to connect via localhost:5433
#database #fly

Showing page 1 of 42

Your competitors are already using AI.
The question is how fast you want to unlock the value.

Don't know where to start?

AI is everywhere but it's unclear which investments will actually move your metrics and which are expensive experiments.

Your data isn't ready

Most AI projects fail at the data layer. Pipelines, quality, access all need work before LLMs can deliver value.

Internal teams are stretched

Your engineers are shipping product. They don't have capacity to also become AI specialists with production-grade experience.

Legacy systems block everything

Aging, undocumented codebases make AI integration slow, risky, and expensive. They need to move first.

Don't worry. We've got you covered.

Start with the audit.