Skip to content

Blog

AI Has Democratized the How

For the longest time, business work was gated by access to the right playbook.

  • How do we hire and onboard better?
  • How do we prepare for compliance?
  • How do we improve sales follow-ups?
  • How do we standardize daily operations?
  • How do we set up useful software or automation?

These were not impossible questions. But they were expensive questions. They needed senior people, consultants, networks, trial and error, or patience to turn scattered documentation into an answer.

AI has cut that path short.

The HOW is mostly sorted now.

The manual is within reach, even far away from traditional centers of capital, hiring, and mentorship.

For years, “I do not have the right resources” was a believable explanation. Sometimes it was true.

AI has changed the meaning of that excuse.

If someone was genuinely blocked because they did not know how to write a process, set up a workflow, or train staff, AI should help.

If they still feel stuck, the problem may not be resources anymore.

Maybe the next missing piece is clarity.

Maybe they need to define the business, choose the customer, take the risk, and carry the responsibility.

AI is exposing the difference between people blocked by access and people ready to act once access improves.

AI can suggest:

  • an HR pipeline
  • a compliance checklist
  • a sales follow-up process
  • an inventory workflow
  • a customer support playbook
  • a software setup

But it does not know your customers, constraints, standards, economics, or appetite for risk.

It can explain how other companies solve a problem. It cannot decide whether that answer fits yours.

The scarce skill is no longer just knowing the procedure. It is knowing the structure in which the procedure must be applied.

That is where experienced people become more important, not less. They can look at an AI-generated answer and say: this is too complex, too risky, good enough, or solving the wrong problem.

Execution has become more abundant. Decision quality has not.

When the how becomes widely available, the advantage shifts toward what and why.

  • What customer is worth serving?
  • What problem is painful enough?
  • What should be standardized?
  • What should be ignored?

AI can shorten the path, but it does not choose the destination.

This is a positive shift. Too many capable people did not have access to the path. AI gives them leverage, whether they run a shop, factory, agency, SaaS company, or local service business.

It also raises the bar for honesty.

If the manual is no longer hidden, the better question is: what do you want to build with it?

The gate is open now. More people can walk through it.

Astro Blog with Nx Monorepo

I had been thinking about migrating my WordPress blog into something more developer-oriented. WordPress is great — battle-tested, popular, and has a plugin for everything — but it’s not built for people like me who want full control over their content and workflow.

What I really wanted was a blog system that would let me:

  • ✍️ Write posts in Markdown
  • 🧳 Work comfortably from my tablet (via GitHub Mobile or Codespaces)
  • ⚙️ Build into static pages (fast + SEO-friendly)
  • 📚 Display posts in reverse chronological order like a traditional blog
  • 🚀 Deploy via CI (GitHub Actions) and host on GitHub Pages or similar tooling

After evaluating a few static site generators, I chose Astro, using the Starlight Rapide theme. The reasons were simple:

  • Fast static builds
  • Clean, minimal layout
  • Native Markdown support
  • Easy to host and maintain
  • Content collections for typed Markdown posts

I already had an Nx workspace (rishabhmhjn-srcs) where I plan to keep all my projects. I added the Astro blog as an app inside apps/rishabhmhjn.com.

Each blog post lives under src/content/blog/, with frontmatter like:

---
title: "My First Post"
pubDate: 2025-07-15
description: "This is the first post on my new Astro blog!"
tags: ["astro", "starter"]
---

This setup makes it super easy to write and commit from anywhere — even on mobile.

I created a GitHub Actions workflow that builds the Astro site and deploys to GitHub Pages. The steps are simple:

  1. Install dependencies
  2. Run astro build (wrapped in Nx)
  3. Deploy dist/projects/rishabhmhjn.com using peaceiris/actions-gh-pages

More at .github/workflows/rishabhmhjn.com.yml

I verified my custom subdomain on GitHub by:

  • Adding a TXT record (_github-challenge-)
  • Setting up a CNAME DNS record pointing astro.rishabhmhjn.com to rishabhmhjn.github.io

It now lives at https://astro.rishabhmhjn.com 🚀

I wanted to be able to perform some level of coding on my tablet and explore tools like codespaces. I am actually writing this blog on my tablet!

Codespaces on tablet

#100daysofcode Day 5: Shared styles in NX #Angular libraries

With another missed day in my challenge, I will work on packaging styles with a library and use them in our newly created Angular app.

It is easier to serve or build styles inside your app with the default configurations. Once you scaffold an application, the styles are available at {projectRoot}/src/styles.scss.

To test it out, let us serve our application.

$ nx serve linkinbio

Now, open http://localhost:4200/ and you can see your app component.

Screenshot-2023-03-14-at-16.53.57

If we make a small change to our styles, we can see that in real-time in our app.

styles.scss
/* You can add global styles to this file, and also import other style files */
p {
font-weight: bold;
}

Screenshot-2023-03-14-at-16.56.07

Great. So our styling works.

This is good for a self-contained application.

However, in the case of large applications, there will be a requirement for sharing styles across your libraries and including them as part of the main application build process.

Creating a shared UI library containing variables, mixins, and shared component-specific styles is helpful.

So we will learn how to include styles from an NX library into our application.

Solution – sharing styles across NX libraries

Section titled “Solution – sharing styles across NX libraries”

Let’s create some styles in our ui-shared library at libs/linkinbio/ui/shared/src/styles/linkinbio-ui-shared

libs/linkinbio/ui/shared/src/styles/linkinbio-ui-shared/_variables.scss
$color-red: red;
libs/linkinbio/ui/shared/src/styles/linkinbio-ui-shared/theme.scss
p {
font-weight: bold;
}

Now, let’s add the instructions to tell angular compiler to set libs/linkinbio/ui/shared/src/styles as one of the paths to look for for our new styles.

To do that, we will add the following stylePreprocessorOptions property to projects/linkinbio/project.json file.

{
...
"styles": ["projects/linkinbio/src/styles.scss"],
"stylePreprocessorOptions": {
"includePaths": ["libs/linkinbio/ui/shared/src/styles"]
},
"scripts": []
...
}

Now, you can easily import the styles into your app:

styles.scss
/* You can add global styles to this file, and also import other style files */
@use 'linkinbio-ui-shared/variables' as ui-shared-variables;
@import 'linkinbio-ui-shared/theme';
// @import '@rishabhmhjn/linkinbio/ui/shared/linkinbio-ui-shared';
p {
font-weight: bold;
color: ui-shared-variables.$color-red;
}

You can see your styles applied in the browser.

Screenshot-2023-03-14-at-17.28.17

It is important to note that I have added a distinct folder, linkinbio-ui-shared, inside the library styles folder. This is because, in the future, it is likely I will need to import styles from other libraries too.

To ensure the names of the shared styles do not collide with one another, I have chosen to namespace them by putting them in their own unique project name.

That’s it! Happy coding 🙂

#100daysofcode Day 4: Break in the challenge & Ng Scaffolding

I had to take a break! I was working from early in the morning and was finishing Statusbrew tasks until late at night before I could get back home to figure out what to write about the blog and then code/screenshot/document and write about it.

I was up until 2 am consistently for the whole week. I was drained a lot on Wednesday, thinking I would write the blog upon waking up, and, predictably, that didn’t happen.

I would only delay this process for a while, but it won’t be canceled. I want to write daily. I want to build up this habit and do this consistently. A cliché but giving up isn’t an option.

I have to mention that writing regularly is one of the things I want to do, me prioritizing Statusbrew tasks, which is for sure would be my priority, is another way to ensure I get done with the complicated Statusbrew tasks so that I can come back to writing as quickly as possible with a more bandwidth.

Another goal of mine is to return to Sales and Marketing by the end of this month. Looking at the current pace of tasks, it seems unlikely I would be done by the end of the month, but the deviation would probably be for about a week. This doesn’t mean that the Statusbrew tasks will entirely disappear. It just means that I would focus on mentoring & quality control in development while spending significantly more time on Sales.

Getting back to the challenge now. Day 4 it is.

Today, I want to set up an Angular application inside our nx monorepo.

My goal in the next few days is to learn the following concepts/frameworks and combine them to build the application:

  1. SSR – Server-Side Rendering ( Angular )
  2. PWA – Progressive Web Application ( Angular )
  3. NestJS
  4. Storybook (with Compodoc )
  5. Testing ( Angular & NestJS )
  6. Railway

While considering the type of application to cover the above, apart from the de-facto todos app, I want to build something that can align with what we can use at Statusbrew. There is one app idea I think may require integration across all the above – Link In Bio.

It has been asked by a few of our clients already. Quite simply, some brands like to share a page on their bio containing some of the most common links to their brand web pages, social profiles, news articles, and dynamic content, such as links to the latest blog posts, giveaways, etc.

There are some great apps already out there. e.g. https://lnk.bio/ is simple yet filled with compelling features like sync with the latest YouTube videos, and stats.

I plan to eventually have similar features more aligned with our clients’ preferences. Thinking about it in detail feels like a year-long project, but we would start with the basics here and add more features as we move along.

Today, we will bootstrap 2 angular entities, a library, and an app, and use the incremental build feature to build both.

We will start by installing nx angular dependency on our project. I initially tried using the console with the following commands but it seems the workflow to initialize the app afterward is not well laid out.

$ yarn add -D @nrwl/angular @angular-devkit/core @angular-devkit/schematics

I had to use the NX Console extension to add the dependency properly. You can find more information here.

Let’s just go ahead and add the nx angular depenency to our project.

Screenshot-2023-03-13-at-00.48.17

Screenshot-2023-03-13-at-00.48.38

Let it go through the process and modify the repository. You will see changes to package.json, nx.json, .gitignore, and .vscode/extenstions.json. You will also find some jest.* files added.

Here are the commands that it seems to have run in the process

$ yarn add -D -W @nrwl/angular
$ yarn nx g @nrwl/angular:init --interactive=false

We will make some changes to the nx.json file related to generator configs that would look like this

{
"generators": {
"@nrwl/angular:application": {
"style": "scss",
"linter": "eslint",
"unitTestRunner": "jest",
"e2eTestRunner": "cypress",
"strict": true,
"prefix": "rm",
"standalone": true
},
"@nrwl/angular:library": {
"linter": "eslint",
"unitTestRunner": "jest",
"strict": true,
"prefix": "rm",
"style": "scss",
"changeDetection": "OnPush",
"standalone": true
},
"@nrwl/angular:component": {
"style": "scss",
"prefix": "rm",
"changeDetection": "OnPush",
"standalone": true
}
}
}

Now, we will generate a ui library for linkinbio. I would be using the UI to create the lib, which would use the following command.

$ yarn nx generate @nrwl/angular:library shared \
--buildable \
--directory=linkinbio/ui \
--no-interactive

Our new library is now available at /libs/linkinbio/ui/shared.

You can now quickly build the library using the nx-console, project.json or with the following command

$ nx build linkinbio-ui-shared

Now, let’s quickly create a new application. Again, I will use nx-console to initialize the app, which eventually uses the following command.

$ nx generate @nrwl/angular:application linkinbio

I learned that we must manually add the missing @angular-eslint/eslint-plugin since it is not added after scaffolding. Let’s add that package too.

$ yarn add -D @angular-eslint/eslint-plugin

Now, we can launch the newly created app

$ nx serve linkinbio

Let’s make the following changes to the app

  1. Add imports: [LinkinbioUiSharedComponent] to app.component.ts
  2. Update the content of app.component.html to just [ ]
  3. Remove nx-welcome.component.ts

You can see our app successfully imported a component from a library project.

Screenshot-2023-03-13-at-01.26.07

Let us build the app to ensure proper library imports as well as incremental builds.

$ nx build linkinbio
✔ 1/1 dependent project tasks succeeded [0 read from cache]
Hint: you can run the command with --verbose to see the full dependent project outputs
———————————————————————————————————————————————————————————————————————————————————————————————————————————————————
> nx run linkinbio:build:production
✔ Browser application bundle generation complete.
✔ Copying assets complete.
✔ Index html generation complete.
Initial Chunk Files | Names | Raw Size | Estimated Transfer Size
main.068e18dfd9616db8.js | main | 82.28 kB | 25.05 kB
polyfills.a0e551d9f0aaa365.js | polyfills | 33.09 kB | 10.65 kB
runtime.dfd26405657e5395.js | runtime | 896 bytes | 509 bytes
styles.ef46db3751d8e999.css | styles | 0 bytes | -
| Initial Total | 116.24 kB | 36.19 kB
Build at: 2023-03-13T00:27:27.824Z - Hash: 0e4718ad16d04a5c - Time: 5027ms
———————————————————————————————————————————————————————————————————————————————————————————————————————————————————
> NX Successfully ran target build for project linkinbio and 1 task it depends on (8s)

You will see that the initial instructions are to build the library before building the app. In the end, the app builds successfully

This will be it for tonight. We will work with Angular Material and Storybook tomorrow to create UI components.

See you tomorrow.

#100daysofcode Day 3: Shared eslint-plugin

Today’s challenge will be a quick one.

I would create and use a custom eslint-plugin in our upcoming libraries & projects. Our goal with the eslint-plugin library will be to share reusable eslint configurations.

Before we generate any new library, I want to change the configuration of nx to add new libraries to the /libs folder and new projects to the /projects folder. Update the nx.json file to reflect the following:

{
.
"workspaceLayout": {
"appsDir": "projects",
"libsDir": "libs"
}
.
}

Next, we will generate 2 ui libraries by running the following commands.

$ nx generate @nrwl/js:library lib1 --unitTestRunner=none --bundler=esbuild --directory=ui --no-interactive
$ nx generate @nrwl/js:library lib2 --unitTestRunner=none --bundler=esbuild --directory=ui --no-interactive

Alternatively, we can generate the new libraries using the nx-console extension gui.

Screenshot-2023-03-07-at-23.56.08

Let’s learn about some of the newly generated library files.

This will be the root Typescript configuration for our project. All future libraries or projects will be extending this configuration. The important section to note is paths where we can see our newly added library mentioned.

{
...
"paths": {
"@rishabhmhjn/ui/lib1": ["libs/ui/lib1/src/index.ts"],
"@rishabhmhjn/ui/lib2": ["libs/ui/lib2/src/index.ts"]
}
...
}

This critical file contains the configurations and commands we would like to run via our nx console.

If you have installed the nx-console extension, you will see small links to run the commands (or targets) directly from the project.json file.

Screenshot-2023-03-08-at-00.49.22

We can go ahead and run the build command directly from the project.json file or type the following command in the console.

$ nx run ui-lib2:build

The library will be compiled to the /dist folder.

There will be 3 files related to eslint that are added to our repository.

/.eslintrc.json
/.eslintignore
/libs/ui-lib{1,2}/.eslintrc.json

There are 2 essential things to note about the root .eslintrc.json.

  1. nx’ eslint-rule: @nrwl/nx/enforce-module-boundaries This handy rule triggers warnings if we have circular dependencies in our libraries or projects. You can read more about the rule here
  2. By default, the whole repository is marked as ignored with “ignorePatterns”: [“**/*”] . Every individual library or project does the opposite and marks themselves as not ignored in their respective .eslintrc.json configurations

Besides ignorePatterns in the .eslintrc.json file, we also see a .eslintignore file that contains more file patterns to be ignored globally.

Now, we will quickly test the @nrwl/nx/enforce-module-boundaries rule by updating the following 2 files:

/libs/ui/lib1/src/lib/ui-lib1.ts

import { uiLib2 } from '@rishabhmhjn/ui/lib2';
export function uiLib1(): string {
return 'ui-lib1';
}
export function testBoundaries() {
return uiLib2();
}

/libs/ui/lib2/src/lib/ui-lib2.ts

import { uiLib1 } from '@rishabhmhjn/ui/lib1';
export function uiLib2(): string {
return 'ui-lib2';
}
export function testBoundaries() {
return uiLib1();
}

We can see that the eslint extension will be showing an error:

Screenshot-2023-03-08-at-00.37.19

This means our eslint setup is working!

We can also see that the warning goes off if we deactivate the rule in the root .eslintrc.json config

{
"@nrwl/nx/enforce-module-boundaries": [
"off",
{...}
}

Now we can create our shared eslint-plugin library where will move the @nrwl/nx/enforce-module-boundaries.

Let’s create a new library: @rishabhmhjn/eslint-config.

$ nx generate @nrwl/js:library eslint-plugin \
--unitTestRunner=none \
--bundler=esbuild \
--directory=etc \
--importPath=@rishabhmhjn/eslint-plugin \
--js \
--no-interactive

It is essential to know that eslint-plugin can only use configs exposed from other npm packages and not via the typescript’s paths configuration.

Since we will add our newly added library as a linked package directly into our root package.json, we can choose to remove the path entry from the tsconfig.base.json file.

tsconfig.base.json
{
"paths": {
"@rishabhmhjn/eslint-plugin": ["libs/etc/eslint-plugin/src/index.ts"], // You may remove this line
"@rishabhmhjn/ui/lib1": ["libs/ui/lib1/src/index.ts"],
"@rishabhmhjn/ui/lib2": ["libs/ui/lib2/src/index.ts"]
}
}

Our package name is already set to what we need, so we do not require any change here.

{
"name": "@rishabhmhjn/eslint-plugin",
"version": "0.0.1",
"type": "commonjs"
}

We will add the new library as a yarn-linked package with the following command.

$ yarn add link:./libs/etc/eslint-plugin -D

This will create a new entry "@rishabhmhjn/eslint-plugin": "link:./libs/etc/eslint-plugin" in the devDependencies of the root package.json file.

Now let’s make the following changes to our repository:

  • Update libs/etc/eslint-plugin/package.json This exposes our main index.js file that will export our shared eslint configs.
{
"name": "@rishabhmhjn/eslint-plugin",
"version": "0.0.1",
"type": "commonjs",
"main": "src/index.js" // Add this line
}
  • Remove libs/etc/eslint-plugin/src/lib/etc-eslint-plugin.js We don’t need this file

  • Add libs/etc/eslint-plugin/src/lib/configs/nx.js This is the config containing the set of rules bunched together. We can see that this is the javascript equivalent of the rule in the current root .eslintrc.json file.

module.exports = {
plugins: ['@nrwl/nx'],
overrides: [
{
files: ['*.ts', '*.tsx', '*.js', '*.jsx'],
rules: {
'@nrwl/nx/enforce-module-boundaries': [
'error',
{
enforceBuildableLibDependency: true,
allow: [],
depConstraints: [
{
sourceTag: '*',
onlyDependOnLibsWithTags: ['*']
}
]
}
]
}
}
]
};
  • Update libs/etc/eslint-plugin/src/index.js This is how we name and export the configs. Currently, we are exporting the nx config as plugin:@rishabhmhjn/eslint-plugin/nx or short plugin:@rishabhmhjn/nx
module.exports = {
configs: {
nx: require('./lib/configs/nx')
}
};

Once these changes are made, we can remove the rule from the root config and extend the config we just exposed with our new eslint-plugin library.

  • Update .eslintrc.json
{
"root": true,
"ignorePatterns": ["**/*"],
"plugins": ["@nrwl/nx"],
"extends": ["plugin:@rishabhmhjn/eslint-plugin/nx"],
"overrides": [
{
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
"rules": {}
},
{
"files": ["*.ts", "*.tsx"],
"extends": ["plugin:@nrwl/nx/typescript"],
"rules": {}
},
{
"files": ["*.js", "*.jsx"],
"extends": ["plugin:@nrwl/nx/javascript"],
"rules": {}
}
]
}

Now we can go back to the libs/ui/lib1/src/lib/ui-lib{1,2}.ts files and still see that the same lint error.

Except for this time, the rule triggering this error comes from our new eslint-plugin library, not the rule inside the root .eslintrc.json.

Hope you found this helpful. This was a very crude introduction to setting up an eslint plugin, but we will explore this further tomorrow along with

  1. Setting up an angular web app project
  2. Add a few eslint configs and implement them differently across libs
  3. Add lint-staged config with a husky pre-commit hook

See you again tomorrow.