Skip to main content

· 2 min read

Last year, I was a coding enthusiast, fueled by new projects. From building a personal design system to crafting a code generation tool and even diving into a blog, I was on a roll. This year, things shift beautifully as we welcome our first child! The thrill of creating remains, but it's now intertwined with the immense joy and responsibility of becoming a dad.

Balancing work, personal projects, connections, and family becomes a whole new challenge. While my 10+ years in coding instilled time management skills, fatherhood adds a unique dimension. The urge to code late nights is strong, but I'm learning to adjust. I'm savoring the quiet moments now, preparing for the exciting (and let's be honest, potentially sleep-deprived!) future.

Are my projects on hold? Absolutely not! They're evolving with me. I'm embracing the opportunity to find new ways to work on them in smaller, more manageable bursts. Maybe I'll squeeze in design system work during naptime, code during lunch breaks, or dedicate focused evenings to blog writing.

This transition isn't just about juggling time; it's about a change of heart. Fatherhood isn't a pause in my journey, but a beautiful new chapter woven into the fabric of my life. It's about discovering new passions, finding creative outlets that fit this new rhythm, and sharing my knowledge and experiences with the next generation, hopefully inspiring them to build amazing things too.

So here's to the messy, joyful adventure ahead! I might trade late nights for diaper duty, but the fire of passion still burns bright. And who knows, maybe someday my little one will join me in exploring the world of code, building something incredible together.

· 8 min read

Basics

Types: The core of TypeScript. You define the type of your variables.

let name: string = 'Alex';
let age: number = 25;

Interfaces: These let you define the shape of objects.

interface Person {
name: string;
age: number;
}

let user: Person = { name: 'Tom Yum', age: 25 };

Functions: You can specify types for parameters and return values.

function greet(name: string): string {
return 'Hello, ' + name;
}

Classes: TypeScript supports modern JavaScript classes with a bit extra.

class Animal {
constructor(public name: string) {}
move(distance: number): void {
console.log(`${this.name} moved ${distance}m.`);
}
}

Generics: These provide a way to create reusable components.

function identity<T>(arg: T): T {
return arg;
}

Namespace & Modules

Modules: Recommended over namespaces for most use cases. Great for structuring large-scale applications and managing dependencies.

// In file math.ts
export function add(x: number, y: number): number {
return x + y;
}

// In another file
import { add } from './math';
console.log(add(5, 3));

Namespaces: An older TypeScript feature for organizing code. Good for internal organization of large codebases.

namespace MyApplication {
export class MyClass {
/* ... */
}
}

Dynamic Import Expressions: TypeScript supports dynamic imports, allowing you to import modules dynamically.

async function loadModule() {
const myModule = await import('./MyModule');
const myModuleInstance = new myModule.MyClass();
}

Module Augmentation: Modify existing module declarations to add new functionality or modify existing one.

// Extending an existing module
declare module 'module-name' {
export interface ModuleInterface {
newFunction(): void;
}
}

Advanced Features

Union Types: Combine multiple types into one. Super useful for variables that can hold more than one type.

let mixed: number | string;
mixed = 'hello'; // OK
mixed = 42; // Also OK

Type Aliases: Create a new name for a type. It's like making a shortcut.

type StringOrNumber = string | number;
let value: StringOrNumber;

Enums: A way to define a set of named constants. Makes your code more readable.

enum Color {
Red,
Green,
Blue,
}
let c: Color = Color.Green;

Tuples: Arrays where the type of a fixed number of elements is known, but doesn't have to be the same.

let x: [string, number];
x = ['hello', 10]; // OK

Optional Chaining and Nullish Coalescing: New JavaScript features that TypeScript supports for safer code.

#Optional Chaining: Safely access deeply nested properties.

const adventurer = {
name: 'Alice',
cat: {
name: 'Dinah',
},
};
const catName = adventurer.cat?.name;

#Nullish Coalescing: Deal with null or undefined values elegantly.

const input = '';
const storedData = input ?? 'Default';

Advanced Types

Intersection Types: Combine multiple types into one. It's like extending interfaces, but more flexible.

type Employee = {
name: string;
startDate: Date;
};

type Manager = Employee & {
reports: Employee[];
};

Mapped Types: Create types based on existing types. They're like a dynamic way to generate types.

type Readonly<T> = {
readonly [P in keyof T]: T[P];
};

type ReadonlyEmployee = Readonly<Employee>;

Conditional Types: Types that change based on conditions.

type Check<T> = T extends string ? 'String' : 'Other';
type TypeCheck = Check<string>; // 'String'

Utility Types: TypeScript provides several utility types to modify existing types, like Partial, Readonly, and Record.

type PartialEmployee = Partial<Employee>;
type ReadonlyEmployee = Readonly<Employee>;

Indexed Access Types: Allow you to access the type of a property of another type.

type Person = { name: string; age: number };
type PersonName = Person['name']; // string

Index Signatures: Useful when you don't know all the property names of an object.

interface StringMap {
[key: string]: string;
}
const obj: StringMap = { name: 'Alice', job: 'Developer' };

Type Guards: Helps in narrowing down the type of an object within a conditional block.

function isFish(pet: Fish | Bird): pet is Fish {
return (pet as Fish).swim !== undefined;
}

Advanced Types Utility Functions

Partial, Readonly, Record, and Pick: These built-in utility types offer shortcuts for common type transformations.

type PartialPerson = Partial<Person>;
type ReadonlyPerson = Readonly<Person>;
type PersonRecord = Record<string, Person>;
type PickedPerson = Pick<Person, 'name' | 'age'>;

Exclude, Extract, NonNullable: These utilities help in manipulating union types and nullable types.

type T0 = Exclude<'a' | 'b' | 'c', 'a'>; // "b" | "c"
type T1 = Extract<'a' | 'b' | 'c', 'a' | 'f'>; // "a"
type T2 = NonNullable<string | null | undefined>; // string

Omit Type: Removes keys from a type, creating a new type.

type OmitAge = Omit<Person, 'age'>;

ReturnType: Constructs a type consisting of the return type of a function.

type ExampleFunction = () => number;
type FunctionReturnType = ReturnType<ExampleFunction>; // number

Advanced Type Manipulation

Type Assertions: Similar to type casting in other languages, allows you to override the inferred type.

let someValue: unknown = 'this is a string';
let strLength: number = (someValue as string).length;

Discriminated Unions: Useful in scenarios where you have types that could be several things.

interface Circle {
kind: 'circle';
radius: number;
}

interface Square {
kind: 'square';
sideLength: number;
}

type Shape = Circle | Square;

function handleShape(shape: Shape) {
if (shape.kind === 'circle') {
// handle circle
} else {
// handle square
}
}

Type Predicates: Used to narrow types using functions.

function isCircle(shape: Shape): shape is Circle {
return shape.kind === 'circle';
}

Advanced Function Types

Overloads: TypeScript allows function overloading for more flexible code.

function greet(name: string): string;
function greet(age: number): string;
function greet(value: string | number): string {
if (typeof value === 'string') {
return `Hello, ${value}`;
} else {
return `Age: ${value}`;
}
}

Rest Parameters and Spread Operator: Handy for functions with a variable number of arguments.

function multiply(n: number, ...m: number[]): number[] {
return m.map((x) => n * x);
}

Decorators

Special syntax for adding annotations and a meta-programming syntax for class declarations and members.

Class Decorators: Applied to the constructor of a class and can be used to observe, modify, or replace a class definition.

function sealed(constructor: Function) {
Object.seal(constructor);
Object.seal(constructor.prototype);
}

@sealed
class BugReport {
type = 'report';
title: string;

constructor(t: string) {
this.title = t;
}
}

Method Decorators: Applied to method declarations and can be used to observe, modify, or replace method definitions.

function enumerable(value: boolean) {
return function (
target: any,
propertyKey: string,
descriptor: PropertyDescriptor
) {
descriptor.enumerable = value;
};
}

class Greeter {
greeting: string;
constructor(message: string) {
this.greeting = message;
}

@enumerable(false)
greet() {
return 'Hello, ' + this.greeting;
}
}

Parameter Decorators: Used to annotate or modify the behavior of method parameters.

function logParameter(target: Object, propertyName: string, index: number) {
// logic to log or modify parameter
}

class Greeter {
greeting(name: string, @logParameter message: string): string {
return `${name} says: ${message}`;
}
}

Accessor Decorators: Applied to getters/setters.

function configurable(value: boolean) {
return function (
target: any,
propertyKey: string,
descriptor: PropertyDescriptor
) {
descriptor.configurable = value;
};
}

class Point {
private _x: number;
private _y: number;

constructor(x: number, y: number) {
this._x = x;
this._y = y;
}

@configurable(false)
get x() {
return this._x;
}

@configurable(false)
get y() {
return this._y;
}
}

TypeScript with Async/Await

TypeScript supports modern JavaScript features like async/await, making working with asynchronous code much easier.

Async Functions: An async function is a function declared with the async keyword. It can contain await expressions.

async function fetchData(): Promise<Data> {
const response = await fetch('https://api.example.com/data');
const data: Data = await response.json();
return data;
}

Error Handling: Use try-catch blocks for error handling in async functions.

async function safeFetchData(url: string): Promise<Data | null> {
try {
const response = await fetch(url);
const data: Data = await response.json();
return data;
} catch (error) {
console.error('Fetch error:', error);
return null;
}
}

Promise.all: When you have multiple asynchronous operations that can run concurrently, use Promise.all to wait for all of them to finish.

async function fetchMultipleUrls(urls: string[]): Promise<Data[]> {
const promises = urls.map((url) =>
fetch(url).then((response) => response.json())
);
return Promise.all(promises);
}

Promise.allSettled: Similar to Promise.all, but waits for all promises to settle, regardless of whether they are fulfilled or rejected.

async function fetchWithAllSettled(urls: string[]): Promise<(Data | Error)[]> {
const promises = urls.map((url) =>
fetch(url)
.then((response) => response.json())
.catch((error) => error)
);
return Promise.allSettled(promises);
}

Async Iterators and Generators: which are great for handling streams of data asynchronously.

async function* asyncGenerator() {
let i = 0;
while (i < 3) {
yield await new Promise(resolve => setTimeout(() => resolve(i++), 1000));
}
}

async function runAsyncGenerator() {
for await (let value of asyncGenerator()) {
console.log(value);
}
}

Working with JSON in TypeScript

When dealing with JSON data, type assertion can be used to ensure the JSON structure matches your expected type.

const data = JSON.parse(jsonString) as MyInterface;

Mixins

Mixins are a way to add functionality to classes. TypeScript supports mixins through its more abstract type system.

type Constructor<T = {}> = new (...args: any[]) => T;

function TimesStamped<TBase extends Constructor>(Base: TBase) {
return class extends Base {
timestamp = Date.now();
};
}

class User {
name = '';
}

const TimesStampedUser = TimesStamped(User);

const user = new TimesStampedUser();
console.log(user.timestamp); // Logs the current timestamp

· 3 min read

Hello, fellow developers! 👋

Today, I want to dive into an architectural approach that can revolutionize the way you handle large-scale projects in Express, TypeScript, Prisma, Zod, and Pino. This approach is all about organizing your project into a modular structure.

What is a Modular Project Structure?

Imagine breaking down your application into smaller, self-contained units - each focusing on a specific feature or domain. This is the essence of a modular project structure. It's like creating a series of mini-applications within your main application, each with its own controllers, services, models, routes, and validation schemas.

Why Go Modular?

The benefits are substantial:

  • Scalability: As your application grows, adding new features becomes easier and more manageable.
  • Maintainability: Making changes or updates is less risky and more straightforward when your code is well-organized.
  • Team Collaboration: Different teams can work on separate modules, reducing code conflicts and improving productivity.

Project Structure

Here's a basic outline of what a modular project might look like:

your-project/

├── src/
│ ├── modules/ # Feature-based modules
│ │ ├── user/ # User module
│ │ │ ├── controllers/ # User controllers
│ │ │ ├── services/ # User services
│ │ │ ├── models/ # User models (Prisma)
│ │ │ ├── routes/ # User routes
│ │ │ └── schemas/ # User validation schemas (Zod)
│ │ │
│ │ ├── product/ # Product module
│ │ │ ├── controllers/
│ │ │ ├── services/
│ │ │ ├── models/
│ │ │ ├── routes/
│ │ │ └── schemas/
│ │ │
│ │ └── ... # Other modules
│ │
│ ├── common/ # Common functionalities and utilities
│ ├── middlewares/ # Global Express middlewares
│ ├── utils/ # Utility functions and helpers
│ ├── app.ts # Express app initialization
│ └── server.ts # Server entry point

├── prisma/ # Prisma ORM files
├── logs/ # Log files
├── tests/ # Test files
├── .env # Environment variables
├── package.json # Project dependencies
├── tsconfig.json # TypeScript configuration
└── README.md # Project documentation

Tips for Success

  • Stay Organized: Keep your modules self-contained and focused.
  • Reuse Code: Utilize the common directory for shared utilities and functions.
  • Test Rigorously: Mirror your tests to match the modular structure for clarity.

Final Thoughts

Remember, this modular approach is a guideline, not a strict rule. Adapt it to suit your project's needs and your team's workflow. The goal is to make your development process smoother, more efficient, and enjoyable.

I encourage you to experiment with this structure in your next project. It might just be the game-changer you need. Happy coding! 💻🚀

· 2 min read

Hey everyone! 👋

Just wanted to share a bit of my journey and hopefully inspire some of you to start building and experimenting on your own.

Back in 2009, when I was diving into web app development, jQuery and its libraries were all the rage. I remember creating my own little library to handle form validation and make tables more manageable. It was a small start, but it got me hooked on building tools.

Fast forward to now, after more than a decade in SaaS development, my focus shifted to solving real-world problems for businesses and people. But the itch to build never really went away.

Recently, I got to play around with web components, and it reignited my passion for creating tools. It's like going back to my roots but with all the experience I've gained.

Now, I'm super excited about my next project: code generation. I had a taste of it years ago while working with my first manager, and now I'm ready to take it on again.

And here's something important I've learned: people make mistakes, and that's okay. It's part of the process. What really matters is that you don't give up. Try again, learn from your mistakes, and keep moving forward. Every mistake is a lesson that makes you stronger.

So, to anyone out there thinking about building something: Go for it! Start small, learn, and keep growing. You've got this! 💪

· 3 min read

Introduction

Stepping into leadership felt like embarking on a new adventure. Along the way, I've navigated teamwork waters, mingled with clients, and synced up with fellow managers. Here, I unfold my leadership tale, showing it's more about growing relationships, tackling challenges, and evolving with my team than just holding a title.

The Ripple Effect

Being a leader, I quickly realized that every action and decision sends ripples through the team, affecting morale and performance. It's like tossing a pebble into a pond and watching the waves spread out. This insight brought along a sense of responsibility to ensure my actions stir positive vibes.

Embracing Ideas

Each team member comes with a unique bundle of ideas. Creating a space where these ideas flow freely felt like tending to a garden of creativity. Real-life snippet: When a project roadblock came up, a casual coffee-fueled brainstorming session sparked a creative solution, showcasing the magic of collective thinking.

Facing Problems Head-On

Problems are part of the leadership package. Confronting them, tweaking strategies, and hunting for solutions became a daily drill. Every hurdle crossed was a win, a lesson that sharpened our problem-solving skills and tightened our team bond.

Never Stop Learning

Leadership is a learning expedition. Be it through courses, books, or nuggets of wisdom from mentors, every new bit of knowledge has been a handy tool in my managerial toolkit.

Understanding Others

Building a strong team is about getting and appreciating the diverse personalities within. Learning to communicate well with each team member brewed a sense of unity and mutual respect, which in turn bolstered our teamwork.

Celebrating Strengths

Spotting and celebrating the strengths of my team members turned into a rewarding part of leadership. It was about fostering a positive space where everyone could shine and contribute to our shared goal.

Learning from Mistakes

Mistakes were seen as learning launchpads rather than something to fear. It was a lesson in humility and growth, acknowledging the missteps, learning from them, and marching forward with newfound wisdom.

Taking Ownership

Owning up to decisions, and standing by the team during tough times crafted a culture of trust and shared responsibility. It was about sailing together through rough waters towards our shared goals.

Evolving Together

Leadership is about growing together with the team, sharing the triumphs, the learnings from failures, and the thrill of new discoveries. Each day carried new lessons, new hurdles, and new chances to grow.

Conclusion

Leadership is a journey packed with endless learning, nurturing bonds, and personal and team growth. The leader title is more than a badge—it's a pledge to guide the team to success while making sure the journey is rewarding and enjoyable for everyone involved. As this journey rolls on, the experiences along the way keep inspiring and shaping the road ahead.

· 5 min read

Introduction

Let’s dive into an Angular project structure crafted for seamless scalability. This setup, rooted in Domain-Driven Design (DDD) principles, paves the way for micro-frontends and module federation down the line. Learn how to arrange your Angular app for better modularity and maintainability, whether you’re juggling a monorepo or shifting to a polyrepo. This way, your project is geared up to grow along with your needs.

Project Structure

This structure is domain-focused, ensuring modularity and easy navigation.

my-angular-app/
|-- src/
| |-- domains/
| | |-- core/
| | | |-- utils/
| | | |-- auth/
| | | |-- ui/
| | | |-- core-module.ts
| | |-- shared/
| | | |-- components/
| | | |-- services/
| | | |-- shared-module.ts
| | |-- finance-domain/
| | | |-- features/
| | | | |-- income-feature/
| | | | | |-- pages/
| | | | | | |-- summary-page/
| | | | | | |-- detailed-page/
| | | | | |-- components/
| | | | | |-- services/
| | | | |-- expense-feature/
| | | | | |-- pages/
| | | | | | |-- summary-page/
| | | | | | |-- detailed-page/
| | | | | |-- components/
| | | | | |-- services/
| | | |-- shared/
| | | | |-- components/
| | | | |-- services/
| | | |-- finance-module.ts
| | |-- customer-management-domain/
| | | |-- features/
| | | | |-- user-profile-feature/
| | | | | |-- pages/
| | | | | | |-- overview-page/
| | | | | | |-- settings-page/
| | | | | |-- components/
| | | | | |-- services/
| | | | |-- transaction-history-feature/
| | | | | |-- pages/
| | | | | | |-- summary-page/
| | | | | | |-- detailed-page/
| | | | | |-- components/
| | | | | |-- services/
| | | |-- shared/
| | | | |-- components/
| | | | |-- services/
| | | |-- customer-management-module.ts
| |-- assets/
| |-- environments/
| |-- index.html
| |-- main.ts
| |-- styles.css
|-- .angular.json
|-- .gitignore
|-- package.json
|-- tsconfig.json
|-- README.md
|-- angular.json

Key Components

  1. Core Module: This is where utilities, authentication services, and essential UI components live.
  2. Shared Module: This is home to components and services reusable across different domains and features.
  3. Domains: Domains like "finance-domain" and "customer-management-domain" house their own features, shared components, and services.

Advantages

  • Modularity: Easier management, scalability, and maintenance.
  • Separation of Concerns: Cleaner code that’s easier to maintain.
  • Code Reusability: Less duplication and more consistency.
  • Feature-Based Organization: Promotes collaboration among developers.
  • Easy Navigation: Speeds up the development process and boosts productivity.

Going Beyond: Micro-Frontend, Poly-Repos, Libraries, and Module Federation

The domain-driven setup forms a solid base for your Angular project. But as your project balloons and team needs shift, it’s crucial to see how your setup can morph and tap into advanced architectural concepts like Micro-Frontend, Poly-Repos, Private npm Libraries, and Module Federation.

Transitioning to Micro-Frontend

Start by pinpointing the domains in your current structure. Each domain can morph into a micro-frontend, becoming a self-contained unit with its own frontend and backend parts. This move lets you scale and deploy independently, boosting maintainability and scalability.

Embracing Poly-Repos

To amp up code sharing and manageability, think about moving from monorepo to polyrepo setup. This way, you can have separate repositories for core and shared modules. Plus, these modules can turn into private npm libraries, promoting clean separation and effective code reuse across various projects and services.

Harnessing Private npm Libraries

Private npm libraries provide a structured way to encapsulate and share code bits. Your existing core and shared modules can become private npm libraries, making them accessible across your application. This not only elevates code modularity but also simplifies sharing and maintaining common functionality.

Leveraging Module Federation

Webpack 5’s Module Federation feature is a game-changer for optimizing your setup. By adopting Module Federation, you enable dynamic loading of federated modules, allowing seamless sharing of specific components, services, or modules between micro-frontends. This not only trims down load times but also eases development collaboration.

Nx Workspace

Consider Nx Workspace as a handy toolkit for managing monorepos efficiently, especially as your team expands. Nx Workspace smoothes out development by promoting project libraries (libs) for modular organization.

Conclusion

By adopting these strategies, your Angular application smoothly transitions from its current domain-driven architecture to a versatile setup that includes Micro-Frontend, Poly-Repos, Private npm Libraries, and Module Federation. This makeover gears your project to tackle future growth, complexity, and scalability challenges while keeping code neat and organized.

· 3 min read

Kickoff

In the whirlwind of tech projects, good leadership can be your anchor. It's not just about cracking the code; it's about steering your squad smoothly through the storm. Here, we’ll skim through some leadership styles and see where they fit in the tech puzzle.

The Leadership Palette

Leadership isn't a one-size-fits-all deal. Let’s break down a few styles:

Transformational Leadership

This style is all about firing up the team.

  • Being a Transformational Leader:

    • Cook up a vision that gets everyone excited.
    • Give a thumbs up to new ideas and approaches.
    • Walk the talk.
  • When it shines:

    • When you need some out-of-the-box thinking.
    • During change or a project pivot.

Servant Leadership

Putting the team first is the mantra here.

  • Being a Servant Leader:

    • Lend an ear to your team.
    • Clear the roadblocks for them.
    • Build a buddy culture, not a boss culture.
  • When it shines:

    • Teamwork-heavy projects.
    • When you're looking to build trust and camaraderie.

Democratic Leadership

A little bit of “we decide” instead of “I decide.”

  • Being a Democratic Leader:

    • Keep the communication lines wide open.
    • Go for group decisions.
    • Share the wheel of responsibility.
  • When it shines:

    • When many heads are better than one.
    • Projects where everyone’s on the same page.

Autocratic Leadership

When the clock’s ticking, and decisions can’t wait.

  • Being an Autocratic Leader:

    • Call the shots with confidence.
    • Own up to the decisions.
    • Make the what, why, and how crystal clear.
  • When it shines:

    • Crisis scenarios.
    • When the project's on a tight leash.

Situational Leadership for Developers

Switching your leadership style as per the game.

  • Being a Situational Leader:

    • Read the room - what does the project or team need?
    • Don’t shy away from switching gears.
    • Stay nimble, stay adaptable.
  • When it shines:

    • Projects where one size doesn't fit all.
    • When your team’s a mixed bag of talents.

Leveling Up Your Leadership Game

If the leadership bug has bitten you, keep growing. Grab a book, take a course, or find a mentor. Ask for feedback to keep your leadership compass in check.

Wrapping Up

Tech leadership isn't about being the smartest in the room; it’s about enabling others to play their A-game. Try different styles, learn from each, and find what fits you and your team like a glove.

Parting Shots

Your leadership journey is a story in the making. Share your learnings, and who knows, you might just inspire someone to step up and lead. Remember, in the realm of leadership, learning never stops.