Skip to content

A Guide to Custom Angular Attribute Directives

When working inside of Angular applications you may have noticed special attributes such as NgClass, NgStyle and NgModel. These are special attributes that you can add to elements and components that are known as attribute directives. In this article, I will cover how these attributes are created and show a couple of examples.

What are Attribute Directives?

Angular directives are special constructs that allow modification of HTML elements and components. Attribute directives are also applied through attributes, hence the name. There exist other types of directives such as structural directives as well, but we’re just going to focus on attribute directives.

If you’ve used Angular before then you have almost certainly used a couple of the attribute directives I mentioned earlier before. You are not limited to just the built-in directives though. Angular allows you to create your own!

Creating Attribute Directives

Directives can be created using code generation via the ng CLI tool.

ng generate directive <directive_name_here>

This will create a file to house your directive and also an accompanying test file as well. The contents of the directive are very barebones to start with.

Let’s take a look.

import { Directive } from '@angular/core';

@Directive({
  selector: '[appExample]',
})
export class ExampleDirective {
  constructor() {}
}

You will see here that directives are created using a @Directive decorator. The selector in this case is the name of the attribute as it is intended to be used in your templates. The square brackets around the name make it an attribute selector, which is what we want for a custom attribute directive. I would also recommend that a prefix is always used for directive names to minimize the risk of conflicts. It should also go without saying to avoid using the ng prefix for custom directives to avoid confusion.

Now, let’s go over the lifecycle of a directive. The constructor is called with a reference to the ElementRef that the directive was bound to. You can do any initialization here if needed. This element reference is dependency injected, and will be available outside the constructor as well. You can also set up @HostListener handlers if you need to add functionality that runs in response to user interaction with the element or component, and @Input properties if you need to pass data to the directive.

Click Away Directive

One useful directive that doesn’t come standard is a click away directive. This is one that I have used before in my projects, and is very easy to understand. This directive uses host listeners to listen for user input, and determine whether the element that directive is attached to should be visible or not after the click event occurs.

@Directive({
  selector: '[appClickAway]',
})
export class ClickAwayDirective {
  @Output() onClickAway: EventEmitter<PointerEvent> = new EventEmitter();

  constructor(private elementRef: ElementRef) {}

  @HostListener('document:click', ['$event'])
  onClick(event: PointerEvent): void {
    if (!this.elementRef.nativeElement.contains(event.target)) {
      this.onClickAway.emit(event);
    }
  }
}

There are a few new things in this directive we’ll briefly go over. The first thing is the event emitter output onClickAway. A generic directive isn’t going to know how to handle click away behavior by itself as this will change based on your use case when using the directive. To solve this issue, we make the directive emit an event that the user of the directive can listen for.

The other part is the click handler. We use @HostListener to attach a click handler so we can run our click away logic whenever clicks are done. The one interesting thing about this directive is that it listens to all click events since we’ve specified ‘document’ in the first parameter. The reason for this is because we care about listening for clicking anything that isn’t the element or component that the directive is attached to.

If we didn’t do this, then the event handler would only fire when clicking on the component the directive is attached to, which defeats the purpose of a click away handler. Once we’ve determined the element was not clicked, we emit the aforementioned event.

Using this directive makes it trivial to implement click away functionality for both modals and context menus alike. If we have a custom dialog component we could hook it up like this:

<div class="shadow">
  <div class="dialog" appClickAway (onClickAway)="clickAway($event)">
    <h2>Dialog Box</h2>
    <p>This is a paragraph with content!</p>
  </div>
</div>

If you want to see this directive in action, then you can find it in our blog demos repo here.

Drag and Drop Directive

Another useful directive is one that assists with drag and drop operations. The following directive makes elements draggable, and executes a function with a reference to the location where the element was dragged to.

@Directive({
  selector: '[appDragDrop]',
})
export class DragDropDirective implements OnInit, OnDestroy {
  @Output() onDragDrop: EventEmitter<MouseEvent> = new EventEmitter();

  mouseDown$ = new Subject<MouseEvent>();
  mouseUp$ = new Subject<MouseEvent>();
  destroy$ = new Subject();

  constructor(private elementRef: ElementRef) {}

  ngOnInit(): void {
    this.mouseDown$
      .pipe(takeUntil(this.destroy$))
      .pipe(exhaustMap(() => this.mouseUp$.pipe(take(1))))
      .subscribe((event) => {
        if (
          event.target &&
          event.target instanceof Element &&
          !this.elementRef.nativeElement.contains(event.target)
        ) {
          this.onDragDrop.emit(event);
        }
      });
  }

  ngOnDestroy(): void {
    this.destroy$.next(null);
    this.destroy$.complete();
  }

  @HostListener('mousedown', ['$event'])
  onMouseDown(event: MouseEvent): void {
    this.mouseDown$.next(event);
  }

  @HostListener('document:mouseup', ['$event'])
  onMouseUp(event: MouseEvent): void {
    this.mouseUp$.next(event);
  }
}

Just like the previous directive example an event emitter is used so the user of the directive can associate custom functionality with it. RxJs is also utilized for the drag and drop detection. This directive uses the exhaustMap function to create an observable that emits both after a mouse down, and finally a mouse up is done. With that observable, we can subscribe to it and call the drag and drop callback so long as the element that’s dragged on isn’t the component itself.

Note how the mouse down event is local to the component while the mouse up event is attached to the document. For mouse down, this is done since we only want the start of the dragging to be initiated from clicking the component itself. The mouse up must listen to the document since the dragging has to end on something that isn’t the component that we’re dragging.

Just like the previous directive, we simply need to reference the attribute and register an event handler.

<div class="drag-drop-demo" appDragDrop (onDragDrop)="drop($event)">
  Drag me over something!
</div>

Conclusion

In this article, we have learned how to write our own custom attribute directives and demonstrated a couple of practical examples of directives you might use or encounter in the real world. I hope you found this introduction to directives useful, and that it helps you with writing your own directives in the future! You can find the examples shown here in our blog demos repository if you want to use them yourself.

This Dot Labs is a development consultancy that is trusted by top industry companies, including Stripe, Xero, Wikimedia, Docusign, and Twilio. This Dot takes a hands-on approach by providing tailored development strategies to help you approach your most pressing challenges with clarity and confidence. Whether it's bridging the gap between business and technology or modernizing legacy systems, you’ll find a breadth of experience and knowledge you need. Check out how This Dot Labs can empower your tech journey.

You might also like

Angular 17: Continuing the Renaissance cover image

Angular 17: Continuing the Renaissance

Angular 17: A New Era November 8th marked a significant milestone in the world of Angular with the release of Angular 17. This wasn't just any ordinary update; it was a leap forward, signifying a new chapter for the popular framework. But what made this release truly stand out was the unveiling of Angular's revamped website, complete with a fresh brand identity and a new logo. This significant transformation represents the evolving nature of Angular, aligning with the modern demands of web development. To commemorate this launch, we also hosted a release afterparty, where we went deep into its new features with Minko Gechev from the Angular core team, and Google Developer Experts (GDEs) Brandon Roberts, Deborah Kurata, and Enea Jahollari. But what exactly are these notable new features in the latest version? Let's dive in and explore. The Angular Renaissance Angular has been undergoing a significant revival, often referred to as Angular's renaissance, a term coined by Sarah Drasner, the Director of Engineering at Google, earlier this year. This revival has been particularly evident in its recent versions. The Angular team has worked hard to introduce many new improvements, focusing on signal-based reactivity, hydration, server-side rendering, standalone components, and migrating to esbuild and Vite for a better and faster developer experience. This latest release, in particular, marks many of these features as production-ready. Standalone Components About a year ago, Angular began a journey toward modernity with the introduction of standalone components. This move significantly enhanced the developer experience, making Angular more contemporary and user-friendly. In Angular's context, a standalone component is a self-sufficient, reusable code unit that combines logic, data, and user interface elements. What sets these components apart is their independence from Angular's NgModule system, meaning they do not rely on it for configuration or dependencies. By setting a standalone: true` flag, you no longer need to embed your component in an NgModule and you can bootstrap directly off that component: `typescript // ./app/app.component.ts @Component({ selector: 'app', template: 'hello', standalone: true }) export class AppComponent {} // ./main.ts import { bootstrapApplication } from '@angular/platform-browser'; import { AppComponent } from './app/app.component'; bootstrapApplication(AppComponent).catch(e => console.error(e)); ` Compared to the NgModules way of adding components, as shown below, you can immediately see how standalone components make things much simpler. `ts // ./app/app.component.ts import { Component } from '@angular/core'; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'], }) export class AppComponent { title = 'CodeSandbox'; } // ./app/app.module.ts import { NgModule } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; import { AppComponent } from './app.component'; @NgModule({ declarations: [ AppComponent ], imports: [ BrowserModule ], providers: [], bootstrap: [AppComponent] }) export class AppModule { } // .main.ts import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; import { AppModule } from './app/app.module'; platformBrowserDynamic() .bootstrapModule(AppModule) .catch((err) => console.error(err)); ` In this latest release, the Angular CLI now defaults to generating standalone components, directives, and pipes. This default setting underscores the shift towards a standalone-centric development approach in Angular. New Syntax for Enhanced Control Flow Angular 17 introduces a new syntax for control flow, replacing traditional structural directives like ngIf` or `ngFor`, which have been part of Angular since version 2. This new syntax is designed for fine-grained change detection and eventual zone-less operation when Angular completely migrates to signals. It's more streamlined and performance-efficient, making handling conditional or list content in templates easier. The @if` block replaces `*ngIf` for expressing conditional parts of the UI. `ts @if (a > b) { {{a}} is greater than {{b}} } @else if (b > a) { {{a}} is less than {{b}} } @else { {{a}} is equal to {{b}} } ` The @switch` block replaces `ngSwitch`, offering benefits such as not requiring a container element to hold the condition expression or each conditional template. It also supports template type-checking, including type narrowing within each branch. ```ts @switch (condition) { @case (caseA) { Case A. } @case (caseB) { Case B. } @default { Default case. } } ``` The @for` block replaces `*ngFor` for iteration and presents several differences compared to its structural directive predecessor, `ngFor`. For example, the tracking expression (calculating keys corresponding to object identities) is mandatory but offers better ergonomics. Additionally, it supports `@empty` blocks. `ts @for (item of items; track item.id) { {{ item.name }} } ` Defer Block for Lazy Loading Angular 17 introduces the @defer` block, a dramatically improving lazy loading of content within Angular applications. Within the `@defer` block framework, several sub-blocks are designed to elegantly manage different phases of the deferred loading process. The main content within the `@defer` block is the segment designated for lazy loading. Initially, this content is not rendered, becoming visible only when specific triggers are activated or conditions are met, and after the required dependencies have been loaded. By default, the trigger for a `@defer` block is the browser reaching an idle state. For instance, take the following block: it delays the loading of the calendar-imp` component until it comes into the viewport. Until that happens, a placeholder is shown. This placeholder displays a loading message when the `calendar-imp` component begins to load, and an error message if, for some reason, the component fails to load. `ts @defer (on viewport) { } @placeholder { Calendar placeholder } @loading { Loading calendar } @error { Error loading calendar } ` The on` keyword supports a wide a variety of other conditions, such as: - idle` (when the browser has reached an idle state) - interaction` (when the user interacts with a specified element) - hover` (when the mouse has hovered over a trigger area) - timer(x)` (triggers after a specified duration) - immediate` (triggers the deferred load immediately) The second option of configuring when deferring happens is by using the when` keyword. For example: `ts @defer (when isVisible) { } ` Server-Side Rendering (SSR) Angular 17 has made server-side rendering (SSR) much more straightforward. Now, a --ssr` option is included in the `ng new` command, removing the need for additional setup or configurations. When creating a new project with the `ng new` command, the CLI inquires if SSR should be enabled. As of version 17, the default response is set to 'No'. However, for version 18 and beyond, the plan is to enable SSR by default in newly generated applications. If you prefer to start with SSR right away, you can do so by initializing your project with the `--ssr` flag: `shell ng new --ssr ` For adding SSR to an already existing project, utilize the ng add` command of the Angular CLI: `shell ng add @angular/ssr ` Hydration In Angular 17, the process of hydration, which is essential for reviving a server-side rendered application on the client-side, has reached a stable, production-ready status. Hydration involves reusing the DOM structures rendered on the server, preserving the application's state, and transferring data retrieved from the server, among other crucial tasks. This functionality is automatically activated when server-side rendering (SSR) is used. It offers a more efficient approach than the previous method, where the server-rendered tree was completely replaced, often causing visible UI flickers. Such re-rendering can adversely affect Core Web Vitals, including Largest Contentful Paint (LCP), leading to layout shifts. By enabling hydration, Angular 17 allows for the reuse of the existing DOM, effectively preventing these flickers. Support for View Transitions The new View Transitions API, supported by some browsers, is now integrated into the Angular router. This feature, which must be activated using the withViewTransitions` function, allows for CSS-based animations during route transitions, adding a layer of visual appeal to applications. To use it, first you need to import withViewTransitions`: `ts import { provideRouter, withViewTransitions } from '@angular/router'; ` Then, you need to add it to the provideRouter` configuration: `ts bootstrapApplication(AppComponent, { providers: [ provideRouter(routes, withViewTransitions()) ] }) ` Other Notable Changes - Angular 17 has stabilized signals, initially introduced in Angular 16, providing a new method for state management in Angular apps. - Angular 17 no longer supports Node 16. The minimal Node version required is now 18.13. - TypeScript version 5.2 is the least supported version starting from this release of Angular. - The @Component` decorator now supports a `styleUrl` attribute. This allows for specifying a single stylesheet path as a string, simplifying the process of linking a component to a specific style sheet. Previously, even for a single stylesheet, an array was required under `styleUrls`. Conclusion With the launch of Angular 17, the Angular Renaissance is now in full swing. This release has garnered such positive feedback that developers are showing renewed interest in the framework and are looking forward to leveraging it in upcoming projects. However, it's important to note that it might take some time for IDEs to adapt to the new templating syntax fully. While this transition is underway, rest assured that you can still write perfectly valid code using the old templating syntax, as all the changes in Angular 17 are backward compatible. Looking ahead, the future of Angular appears brighter than ever, and we can't wait to see what the next release has in store!...

How to Create Standalone Components in Angular cover image

How to Create Standalone Components in Angular

Angular has become one of the most popular frameworks to build web applications today. One of the key features of the framework is its component-based architecture, which allows best practices like modularity and reusability. Each Angular component consists of a template, a TypeScript class and metadata. In this blog post, we will dive deeper into standalone components, and we will explore the anatomy of an application based on them. For the demo application, we will create a card-like component which can be used to render blog posts in a web application. Prerequisites You'll need to have installed the following tools in your local environment: The latest LTS version of Node.js is recommended. Either NPM or Yarn as a package manager. The Angular CLI tool(Command-line interface for Angular). Initialize the Project Let's create a project from scratch using the Angular CLI tool: `bash ng new demo-angular-standalone-components --routing --prefix corp --style css --skip-tests ` This command will initialize a base project using some configuration options: `--routing`. It will create a routing module. `--prefix corp`. It defines a prefix to be applied to the selectors for created components(`corp` in this case). The default value is `app`. `--style css`. The file extension for the styling files. `--skip-tests`. Disable the generation of testing files for the new project. If you pay attention to the generated files and directories, you'll see an initial project structure including the main application module and component: `txt |- src/ |- app/ |- app.module.ts |- app-routing.module.ts |- app.component.ts ` Creating Standalone Components First, let's create the custom button to be used as part of the Card component later. Run the following command on your terminal: `bash ng generate component button --inline-template --standalone ` It will create the files for the component. The --standalone` option will generate the component as _standalone_. Let's update the button.component.ts` file using the content below. `ts import { Component } from '@angular/core'; import { CommonModule } from '@angular/common'; @Component({ selector: 'corp-button', standalone: true, imports: [CommonModule], template: , styleUrls: ['./button.component.css'] }) export class ButtonComponent { } ` Pay attention to this component since it's marked as standalone: true`. Starting with Angular v15: components, directives, and pipes can be marked as standalone by using the flag standalone`. When a class is marked as standalone_, it does not need to be declared as part of an `NgModule`. Otherwise, the Angular compiler will report an error. Also, imports` can be used to reference the dependencies. > The imports property specifies the standalone component's template dependencies — those directives, components, and pipes that can be used within its template. Standalone components can import other standalone components, directives, and pipes as well as existing NgModules. Next, let's create the following components: card-title`, `card-content`, `card-actions` and `card`. This can be done at once using the next commands. `bash ng generate component card-title --inline-template --standalone ng generate component card-content --inline-template --standalone ng generate component card-actions --inline-template --standalone ng generate component card --inline-template --standalone ` On card-title.component.ts` file, update the content as follows: `ts //card-title.component.ts import { Component } from '@angular/core'; import { CommonModule } from '@angular/common'; @Component({ selector: 'corp-card-title', standalone: true, imports: [CommonModule], template: , styleUrls: ['./card-title.component.css'] }) export class CardTitleComponent { } ` Next, update the card-content.component.ts` file: `ts //card-content.component.ts import { Component } from '@angular/core'; import { CommonModule } from '@angular/common'; @Component({ selector: 'corp-card-content', standalone: true, imports: [CommonModule], template: , styleUrls: ['./card-content.component.css'] }) export class CardContentComponent { } ` The card-actions.component.ts` file should have the content below: `ts // card-actions.component.ts import { Component } from '@angular/core'; import { CommonModule } from '@angular/common'; @Component({ selector: 'corp-card-actions', standalone: true, imports: [CommonModule], template: , styleUrls: ['./card-actions.component.css'] }) export class CardActionsComponent { } ` Finally, the card.component.ts` file should be defined as follows: `ts //card.component.ts import { Component } from '@angular/core'; import { CommonModule } from '@angular/common'; @Component({ selector: 'corp-card', standalone: true, imports: [CommonModule], template: , styleUrls: ['./card.component.css'] }) export class CardComponent { } ` Using Standalone Components Once all Standalone components are created, we can use them without the need to define an NgModule`. Let's update the app.component.ts` file as follows: `ts // app.component.ts import { Component } from '@angular/core'; import { ButtonComponent } from './button/button.component'; import { CardComponent } from './card/card.component'; import { CardTitleComponent } from './card-title/card-title.component'; import { CardContentComponent } from './card-content/card-content.component'; import { CardActionsComponent } from './card-actions/card-actions.component'; @Component({ selector: 'corp-root', standalone: true, imports: [ ButtonComponent, CardComponent, CardTitleComponent, CardContentComponent, CardActionsComponent ], templateUrl: './app.component.html', styleUrls: ['./app.component.css'] }) export class AppComponent { title = 'demo-angular-standalone-components'; } ` Again, this Angular component is set as standalone: true` and the `imports` section specifies the other components created as dependencies. Then, we can update the `app.component.html` file and use all the standalone components. `html Latest Posts Introduction to Angular Angular is a component-based framework for building scalable web applications. View Introduction to TypeScript TypeScript is a strongly typed programming language that builds on JavaScript, providing better tooling at any scale. View ` This may not work yet, since we need to configure the Angular application and get rid of the autogenerated module defined under the app.module.ts` file. Bootstrapping the Application Angular provides a new API to use a standalone component as the application's root component. Let's update the main.ts` file with the following content: `ts // main.ts import { bootstrapApplication } from '@angular/platform-browser'; import { AppComponent } from './app/app.component'; bootstrapApplication(AppComponent); ` As you can see, we have removed the previous bootstrapModule` call and the `bootstrapApplication` function will render the standalone component as the application's root component. You can find more information about it here. Live Demo and Source Code Want to play around with this code? Just open the Stackblitz editor or the preview mode in fullscreen. Conclusion We’re now ready to use and configure the Angular components as standalone and provide a simplified way to build Angular applications from scratch. Do not forget that you can mark directives and pipes as standalone: true`. If you love Angular as much as we do, you can start building today using one of our starter.dev kits or by looking at their related showcases for more examples....

Git Bisect: the Time Traveling Bug Finder cover image

Git Bisect: the Time Traveling Bug Finder

I think it’s safe to say that most of us have been in a situation where we pull down some changes from main and something breaks unexpectedly, or a bug got introduced in a recent deployment. It might not take long to narrow down which commit caused the issue if there’s only a couple of new commits, but if you’re a dozen or more commits behind, it can be a daunting task to determine which one caused it. But can’t I just check each commit until I find the culprit? You could just check each commit individually without any special tools until you find the one that caused the issue, but that can be a very slow process. This is not ideal and is analogous to the reason why linear search isn’t as effective as binary search. As the title suggests, there is a tool that Git provides called “bisect”. What this command does is checks out various commit refs in the tree of the branch you’re currently working in and allows you to mark commits as, “good”, “bad”, or “skip” (invalid / broken build). It does away with the need of having to check each commit individually as it is able to infer if commits are good or bad based on which other commits you have already marked. Git Bisect in Action Let’s imagine a hypothetical scenario where some bug was reported for the software we’re working on. Starting a git bisect` session usually looks like the following example. ` $ git bisect start # While on main. $ git bisect bad # Here we are marking HEAD on main as "bad". $ git bisect good 6e71b3cac15d32fe2d36c270887df9479c25c640 ` In this case, the commit hash in this example comes from a commit that I already know works. In the case where you pull down changes, and only then does something break, you can use whatever commit you were at prior before you pulled them down. If it’s an older bug, then you could check an older tag or two to see if it exists there. Next is the part where we search for the offending commit. Every time you mark a commit, bisect will then navigate to another commit in-between your good and bad starting points using a specialized binary search algorithm. ` $ build # This build succeeds but the bug is present. $ git bisect bad # Mark the commit and navigate to another commit. $ build # This command succeeds and the bug is not present. $ git bisect good $ build # Build fails for a reason unrelated to the issue. $ git bisect skip ` This is the general workflow you will follow when bisecting for its most basic use case, and these commands will be repeated until there are no more revisions left to review. Bisect will try to predict how many steps are left, and let you know every time you mark a commit. Once you are done, you will be checked into the commit that introduced the regression. This assumes that you marked everything accurately! After you are done bisecting, you can quickly return to where you started by running git bisect reset`. How Git Bisect Works Firstly, bisect makes the reasonable assumption that any commits after a bad commit remain bad, and any commits before a good commit remain good. It then continues to narrow down which commit is the cause by asking you to check the middlemost commit, along with some added bias when navigating around invalid commits. Though, that’s not vitally important to understand as a user of the command. The following graphic shows how bisect moves throughout your branch’s history. Bisect becomes incredibly useful when dealing with repositories with a lot of history, or when tracking down the cause of a bug that’s been in a codebase for a long time. It makes it possible to mule over hundreds of commits in fewer than a dozen steps! That’s a lot better than going through commits one-by-one or at random. Limitations It is worth mentioning that bisect isn’t as useful in cases where commits are very large because they incorporate several different changes all bundled together (e.g. squash merges). In an ideal world, each commit in the main branch’s history can be built, and they will implement or fix one thing and one thing only. But in reality, this isn’t always the case. The skip` command is available to help with this scenario, but even with that, it’s possible that a change that caused the bug is in one of those skipped commits; therefore, relying solely on the diff of the determined commit to find the root cause of a bug may be misleading. Conclusion Git bisect is a very useful tool that can dramatically decrease the amount of time it takes to identify the cause of a regression. I would also recommend reading the official documentation on git bisect` as it’s actually quite informative! There are a lot of good examples in here that demonstrate how you can use the command to its full potential....

Software Team Leadership: Risk Taking & Decision Making with David Cramer, Co-Founder & CTO at Sentry cover image

Software Team Leadership: Risk Taking & Decision Making with David Cramer, Co-Founder & CTO at Sentry

In this episode of the engineering leadership series, Rob Ocel interviews David Cramer, co-founder and CTO of Sentry, delving into the importance of decision-making, risk-taking, and the challenges faced in the software engineering industry. David emphasizes the significance of having conviction and being willing to make decisions, even if they turn out to be wrong. He shares his experience of attending a CEO event, where he discovered that decision-making and conflict resolution are struggles even for successful individuals. David highlights the importance of making decisions quickly and accepting the associated risks, rather than attempting to pursue multiple options simultaneously. He believes that being decisive is crucial in the fast-paced software engineering industry. This approach allows for faster progress and adaptation, even if it means occasionally making mistakes along the way. The success of Sentry is attributed to a combination of factors, including market opportunity and the team's principles and conviction. David acknowledges that bold ideas often carry a higher risk of failure, but if they do succeed, the outcome can be incredibly significant. This mindset has contributed to Sentry’s achievements in the industry. The interview also touches on the challenges of developing and defending opinions in the software engineering field. David acknowledges that it can be difficult to navigate differing viewpoints and conflicting ideas. However, he emphasizes the importance of standing by one's convictions and being open to constructive criticism and feedback. Throughout the conversation, David emphasizes the need for engineering leaders to be decisive and take calculated risks. He encourages leaders to trust their instincts and make decisions promptly, even if they are uncertain about the outcome. This approach fosters a culture of innovation and progress within engineering teams. The episode provides valuable insights into the decision-making process and the challenges faced by engineering leaders. It highlights the importance of conviction, risk-taking, and the ability to make decisions quickly in the software engineering industry. David's experiences and perspectives offer valuable lessons for aspiring engineering leaders looking to navigate the complexities of the field....