Brian Weiss – Rōnin Consulting https://www.ronin.consulting Expert Engineers Delivering Superior Software Mon, 13 Apr 2026 18:10:36 +0000 en-US hourly 1 https://wordpress.org/?v=7.0 https://www.ronin.consulting/wp-content/uploads/2022/01/cropped-Logo-Red-100x100-1-32x32.png Brian Weiss – Rōnin Consulting https://www.ronin.consulting 32 32 What an AI-First Feature Build Looks Like From the Inside https://www.ronin.consulting/artificial-intelligence/ai-first-feature-build/ Fri, 10 Apr 2026 18:29:54 +0000 https://www.ronin.consulting/?p=2240

What an AI-First Feature Build Looks Like From the Inside

Many talk about going AI-first or building an AI-first feature, but we’re doing it.

In practice, it’s messier and more interesting than most acknowledge.

Right now, our team is working through an AI-first feature. It’s not a proof-of-concept or experiment, but a real feature with delivery commitments. This process clarified often glossed-over topics when discussing AI in software development, specifically: The technology is capable; the hard part is everything else.

The process habits that don’t want to change

The first thing we ran into wasn’t a technical problem. It was a habit problem.

Teams build up layers of process over the years. They follow a specific structure for how stories are written, who needs to be in the room, and what “done” looks like. Those structures that were created worked well enough and for so long that they stopped being deliberate choices and became reflexes.

I like to think of it as SDLC rust. It’s just processes that have been caked on for so long that, when something comes in and hammers them, saying, “nope, now you can do it another way,” people need time to adjust, even when they genuinely want to change immediately.

And that’s where we are now. AI hammers at the fundamentals, and it hits hard.

For teams with a lot of accumulated rust, it can be very disorienting when you try to knock it off and expect the system to work immediately.

You can’t mandate “AI-first” and expect people to instantly rewire their thinking about the SDLC. The change needs to happen, but it’s gradual, and pretending otherwise sets teams up to feel like they’re failing when they’re just adjusting. As we started navigating this, we found our process for documenting new features was changing, too.

How we’re building the PRD with AI  

One of the most concrete changes in how we’re working is how we define and document what we’re building. Before we started this process, there was no formal PRD. Instead, BAs would jump on calls with Product, trying to compile the ask and translate it into user stories, all while referencing a scattered mix of documents, specs, and artifacts that, together, described the feature being built.

It was decentralized, and creating a shared understanding before coding meant a lot of back-and-forth, context-switching, and reliance on tribal knowledge.

The problem with that model isn’t the intent. It’s variability. You have people in that room with very different levels of domain knowledge, all trying to converge simultaneously, while half of them are multitasking. The output reflects that. And the cost in time, in scheduling overhead, in contractor hours add up fast.

To address these challenges in alignment and documentation, we’ve moved to a different model.

The product owner sits down and has a direct conversation with AI. They spend time answering questions, refining the scope, and surfacing edge cases, and it is from this dialogue that the PRD emerges.

With this process, you go from ten people in a meeting to one person in a focused working session. The resulting document is more coherent. It shows a single, continuous train of thought rather than multiple people seeking alignment.

The PRD is faster, cheaper, and yields a cleaner document. But it isn’t finished the moment it’s approved.”

Gaps do happen. The question is how you handle them.

A skillfully crafted AI-assisted PRD will still have gaps. We expected this, but building a feature using this process made it clear. Even with thorough planning, you can hit implementation and find something missed or assumed.

That’s not a process failure, just the nature of complex software. The goal isn’t to eliminate gaps, but to have a solid process for feeding them back into the PRD as they appear.

What we’ve been building to identify and find these gaps is a feedback loop. When a developer hits a gap during implementation, instead of resolving it informally or making a snap judgment, we use AI tooling to generate a structured ticket. The ticket describes the gap:

  • What was expected?
  • What’s missing?
  • What decision needs to be made?

That ticket goes to the BA or product owner, is evaluated, and, if valid, folds back into the PRD.

The PRD remains accurate throughout development, rather than becoming outdated. This might sound like a small detail, but it’s one of the more important process improvements we’ve made.

Watch out for when AI gets confident about the wrong thing.

Here’s something we ran into that I think every team working this way needs to be prepared for.

AI is great at reading a codebase and creating implementation plans. But when it lacks information about your specific system, it might propose the wrong solution with full confidence. It might fill in gaps based on general patterns rather than signaling uncertainty.

We had a situation in which our system had an established approach to record auditing. It wasn’t specifically documented in the context we’d given the AI. So, when that requirement came up, the AI proposed building a set of new tables and columns to handle it.

But that implementation would be completely redundant with what already existed. If someone without deep knowledge of the system had accepted that output, we would have caused considerable technical debt under the impression we were solving a problem.

The fix isn’t better AI. It’s a better context and a developer who knows the domain well enough to catch what the AI doesn’t.”

Building context is unglamorous work, especially in large codebases that were not originally designed for AI-first development. Documenting these key elements is what separates teams that get true value from AI from those that produce polished but sometimes wrong code.

Takeaways from using AI on a real product

A few things I’ve noticed as we’ve worked through this:

Developers are now asked to own the feature end-to-end, not just coding, but understanding. In AI-first development, you need to clearly describe the business problem for AI and critically evaluate its output.

That requires a deeper understanding of the domain. The developers on our team who are picking up this new process best are the ones who have always treated business knowledge as part of their job. The ones who haven’t been are finding this transition harder.

We’re still figuring out developer speed, QA testing, and maintaining quality. AI-assisted development boosts output, but QA still operates at a human pace. We’ve had to add deliberate pauses so developers can collaborate with QA instead of just producing more.

This isn’t necessarily a bad thing. Pausing can help align the direction of a feature across different roles, which helps maintain the accuracy of our outcome. But we are seeing developers take on more in this process, which leaves roles like Scrum Masters trying to shift where their value is used in the SDLC.

What I’d tell a team starting this tomorrow

Don’t expect perfection, expect collaboration.

You’re going to be changing the engine while the car is moving, because that’s the reality of how technology shifts happen inside organizations with real roadmaps and real commitments. Build grace periods into your process. Build feedback loops. Design for gaps rather than hoping they won’t appear.  We all need to wear multiple hats to make a feature a reality.

Invest in context before you invest in tooling. The AI is only as useful as the information you give it about how your system works and what your business needs. That documentation work is foundational, and only your team can do it.

Put your domain knowledge where it matters most. The developers who know the business as well as they know the code are your most valuable people in this model. Make sure they’re the ones guiding the AI, not the ones watching from the side.

These key takeaways are forming our approach. We’re still figuring this out and that’s the most honest thing I can say. But we’re learning fast, and our lessons are worth sharing.

Want More Content Like This?
Join the Rōnin Recap

Get expert insights on AI, integration, and the future of software — direct from the team at Rōnin Consulting. We’ll send you the good stuff, not spam.

]]>
Azure Service Bus: Scheduling Reliable Messages https://www.ronin.consulting/cloud-technology/azure/azure-service-bus-scheduling-messages/ Sun, 29 May 2022 23:46:09 +0000 https://www.ronin.consulting/?p=993 Introduction To Azure Service Bus

The Azure Service Bus provides developers a simple way to handle when messages are available on your Azure Service Bus Queues. Let’s take a look at how you schedule messages and a simple example of when you would use it.

How Azure Service Bus It Works

When posting a message to the Azure Service Bus, we need to set the ScheduledEnqueueTimeUtc on the message. As the property name implies, the time set on this property should be in UTC. This makes it very easy to tell the Azure Service Bus when the message should appear in the Queue and be available to any subscribers. Let’s look at a quick code example:

    private async Task SendMessage(string queue, string message, 
            DateTimeOffset? enqueueTime = null)
        {
            await using var queueClient = new ServiceBusClient(_connection);

            ServiceBusMessage messageQueueMessage = new ServiceBusMessage(message);

            if (enqueueTime != null) 
                messageQueueMessage.ScheduledEnqueueTime = enqueueTime.Value;

            await queueClient.CreateSender(queue).SendMessageAsync(messageQueueMessage);
        }

To be clear, this is not the time the message gets processed, it’s just when it becomes available in the Queue. Processing of the message will depend on how many messages are in the queue.

Real-Life Example

Let’s say within your solution you have an Azure App Service hosting your API that posts messages on your Azure Service Bus. Listening to a Queue on the bus is an Azure Function with a Service Bus Trigger. When our Azure Function is triggered, it will send out a text message immediately.

Service Bus Graphic 1

Over a few weeks, the business receives feedback that end users would prefer to receive text messages after a certain time. So easy enough, when your API posts messages to the Azure Service Bus, developers set the ScheduledEnqueueTimeUtc to the appropriate time.

Checking To See If It Works

Now that we have messages scheduled, we can look inside our Azure Service Bus, select the Queue the messages are in, and check out how many are scheduled.

Azure Service Bus Queue

Conclusion

As you can see while looking into Azure Service Bus above it’s not as complicated as you might believe. It is a simple way to handle messages in the queue, and simple to execute. To learn more about Azure Service Bus or any other Azure services, contact a Rōnin today. 

About Rōnin Consulting – Rōnin Consulting provides software engineering and systems integration services for healthcare, financial services, distribution, technology, and other business lines. Services include custom software development and architecture, cloud and hybrid implementations, business analysis, data analysis, and project management for a range of clients from the Fortune 500 to rapidly evolving startups. For more information, please contact us today.

]]>
Angular: Managing RxJS Observable Subscriptions with UntilDestroy https://www.ronin.consulting/front-end/angular-managing-rxjs-observable-subscriptions-with-untildestroy/ Fri, 13 May 2022 14:39:10 +0000 https://www.ronin.consulting/?p=947  

Unsubscribing from the Subscription: What’s The Big Deal?

Whether you’re listening for events from the Router, listening for changes in a FormControl, or making a request to an API with their HttpClient, if you’ve developed in Angular, you’ve interacted with or subscribed to an RxJS Observable. But do you know why and when you’re responsible for unsubscribing from the Subscription?

Why We Unsubscribe

The goal of unsubscribing is to prevent memory leaks.  When debugging code, memory leaks are subtle and easily missed.  In Angular’s case, it also requires a bit of extra knowledge to know when you’re responsible for the Subscription and when Angular is.

When Angular Handles The Subscription

Angular in some cases is nice enough to handle unsubscribing, for example, if you’re making an API call using their HttpClient:

httpClient.get("/v1/heroes").subscribe((heroes:IHero[])=>{});

If you’re using the async Pipe in your HTML:

<app-heroes-list [heroes]="heroes$ | async"></app-heroes-list>

Angular will handle it. But there are several times you’re responsible for unsubscribing.

When The Developer Handles The Subscription

As a developer, you’re responsible for a Subscription in several cases:

  • Subscribing to events from the Router
  • Subscribing to valueChanges from an AbstractControl (ex. ReactiveForms FormGroup, FormArray, FormControl)
  • RxJS Observables and long-lived Operators (ex. Subjects, timer, interval, etc.)

And in these cases, you’ll see a few approaches that developers use to manage them.

Approach One: Assignment and ngOnDestroy

The most basic approach you can take is to store the Subscription and then unsubscribe from it when a Component’s ngOnDestroy lifecycle method is invoked.

@Component(...)
export class HeroesComponent implements OnInit, OnDestroy {

  private mySubscription: Subscription;

  constructor() { }

  private ngOnInit():void {

    this.mySubscription = interval(1000).subscribe(() => { 
         // Do something here. 
    });

  }

  private ngOnDestroy():void {

   this.mySubscription.unsubscribe();

  }
}

Feels like a lot of work. In classes where multiple subscriptions are being made, you could have more variables to hold them or change your mySubscription:Subscription into an array but that doesn’t make for an elegant approach.

Approach Two: RxJS takeUntil()

The takeUntil operator helps us get a bit further in making a clean break with our Subscription. Passing it in as an operator will allow it to mirror the source Observable until a notifier emits.

Once the notifier emits, the Observable stops mirroring and completes. Let’s update our previous example:

@Component(...)
export class HeroesComponent implements OnInit, OnDestroy {

  private notifier$:Subject<boolean> = new Subject<boolean>();

  constructor() { }

  private ngOnInit():void {

    interval(1000).pipe(takeUntil(this.notifier$)).subscribe(() => { 
         // Do something here. 
    });

  }

  private ngOnDestroy():void {

   this.notifier$.next(true);
   this.notifier$.complete();

  }
}

This approach is a bit cleaner, we avoid tracking any number of subscriptions but still have a bit more code just around the management of the notifier. With the next approach, we can make this even easier.

Approach Three: ngneat/until-destroy(Until Destroy) 

If you haven’t come across this operator yet, you definitely need to check out this repo. It’s called ngneat/until-destroy (GitHub Repo) and it’s going to help us clean up our code. In their latest version (as of this article), they provide a decorator for your Component and an operator. Let’s update our previous example one more time:

@UntilDestroy()
@Component(...)
export class HeroesComponent implements OnInit {
 
  constructor() { }

  private ngOnInit():void {

    interval(1000).pipe(untilDestroyed(this)).subscribe(() => { 
         // Do something here. 
    });

  }
}

Note the placement and order of the @UntilDestroy decorator and the argument we pass into the operator. Now we no longer need any class-level variables or code in the ngOnDestroy to handle our Subscription. That’s it!

UntilDestroy:  Until Next Time

Having worked with Angular and RxJS for years, the ngneat/until-destroy library was a breath of fresh air. It’s definitely my preferred approach to Subscription management and hopefully, you’ll find it useful too! If you do have any other questions about his series of code, stay tuned for more random finds in the Angular world, or if you would like to speak directly to a Ronin, contact us today! Our team has been working in Angular for years, and can answer any questions you may have! 

]]>
Angular: You May Have Missed This https://www.ronin.consulting/front-end/angular-you-may-have-missed-this/ Mon, 04 May 2020 02:05:56 +0000 http://www.ronin.consulting/?p=575 If you’ve built an Angular application before, you’ve probably wondered if there are better ways to accomplish common development tasks. When working, we tend to form habits and continually transfer those habits to new platforms without a second thought. But let’s take a look at some ways to make our development lives easier.

Simplifying Style Paths

Have you ever seen relative path imports in SCSS files associated with your Angular @Component? For example:

@import "../../../theme-variables";

Gets kind of painful with a complex project structure. Even worse when you try to refactor that structure. So what’s the preferred way to import SCSS files? The stylePreprocessorOptions property in your angular.json.

The stylePreprocessorOptions allows you to include additional base paths that Angular will pull in when compiling.

"stylePreprocessorOptions": {
  "includePaths": [
    "src/theme"
  ]
}

So now, when your project compiles, imports are simplified into:

@import "theme-variables";

Angular CLI

More than likely, you’ve had some experience with the Angular CLI if you’ve worked with any Angular project. But if you have only heard about it but don’t leverage it, you might want to consider getting familiar with what it offers.

The Angular CLI is a command-line interface that helps initialize, develop, scaffold, and maintain Angular applications. The CLI is a bit more helpful than some of the IDE plugins you may find out there and became even more powerful as of Angular version 6. Let’s take a look at some of the functionality provided by the CLI.

ng generate

Are you still finding yourself creating Angular application classes by hand? With the CLI, there’s a simplified approach using the below command.

ng g component <name>

//ex: ng g component todo

Some things to remember when running these commands.

  • Each class that the CLI can generate has its own set of flags. For example, running the above will create an @Component called todo.component.ts and will add it to the root @NgModule. So when creating a new @Component with the CLI, be sure to use the –module flag to specify which Feature Module it should be a part of.
  • Generation will create any necessary files. For example, generating an @Component will also create the associated Template, Spec, and SCSS files.
  • Run the command in the directory you’d like for the generation to produce its classes in.

ng add

The ng add <package> utilizes your package manager to install dependencies. Those dependencies can have their own installation script which can update your project with configuration changes and additional dependencies. For example, running npm install <package> will install the dependency, whereas the ng add <package> will install the dependency and configure your angular.json

ng update

Given your package.json, the ng update <package> recommends updates to your application. It will help with dependency versioning, and if one of those dependencies provides an update script using schematics, will even update your code.

Feature Modules

Personally, I continually toil over the best structure when building Angular applications. Should this live here?

To help developers organize their code, they introduced the concept of Feature Modules. This best practice helps logically group code into buckets:

  • Domain
  • Routed
  • Routing
  • Service
  • Widget

Within these groupings, Angular has done a great job of helping guide developers with what should be included in each type of Feature Module. The goal here being a clear separation of concerns for reusable, maintainable, and extensible code.

Build Angular Applications The Easy Way 

There are a lot of little tidbits to pick up along the way when building Angular applications. Though their website is a gold mine of documentation, it doesn’t always provide a short list of things to look at. In the future, I hope to provide more in-depth posts for all you Angular fans. Keep checkin’ back or contact us directly to speak to a Rōnin! 

]]>
Quality Reporting with Azure Functions & Twilio SendGrid https://www.ronin.consulting/cloud-technology/azure/reporting-with-azure-functions-twilio-sendgrid/ Mon, 27 Apr 2020 13:59:00 +0000 http://www.ronin.consulting/?p=566 As a developer, especially one who loves designing UI/UX, hearing “We need this Reporting Feature built” brings me so much excitement. The first thing that comes to my mind is typically data visualization. There will be cool graphs, and lots of customization of what a user can select, and when it’s complete, users will sing your name in praise.

But then, reality sets in when you find out what the end result really is–an Excel document. You may think, “Well, that’s not really that fun,” but I had a lot of fun designing and building this feature. So, let’s walk through how it went end-to-end and some reporting with Azure functionality I was able to implement!

Requirements Of This Build

As with everything we build, there is always a set of requirements that must be met. In our case, it was a pretty simple set:

  • The Report being created targets Work Load for the system.
  • The result is an Excel document, which must contain defined columns.
  • Users need a way to generate the report.
  • Users need a way to save configured properties they used to generate the report so they can quickly re-run it later.

So far, straightforward. But let’s view it from a higher level and see how these Azure features need to fit in.

Architectural Guidelines

From the business perspective, the end result is very simple: “We need this,” but from the development perspective, there are quite a few layers to account for. For this particular project we have the following assumptions/limitations:

  • Uses Microsoft Azure.
  • Fits into a Microservice Architecture.
  • Reports may contain large amounts of data.
  • Currently doesn’t leverage a Data Warehouse.
  • Minimal increase to the project’s administrative footprint.
  • The potential for additional reports down the road.

Given the above, I dug into the Azure toolbox to develop the best approach.

The Approach

So, where to begin? First and foremost, I diagramed out what parts and pieces needed to be involved with the Reporting Feature.

To diagram the setup of the feature.

Reporting UI

To get the report kicked off, we needed a set of pages to support the new functionality. In our existing Angular SPA, we would introduce the following views:

  • A grid of saved reports with CRUD operations.
  • A grid of report jobs to act as an audit table.
  • A create/edit report form.

API Management Service

The existing Angular SPA accesses all of its APIs via an Azure Management Service. The Service is a great way to provide access to public-facing APIs while giving you control over requests made to them via policies. Here we can control validating who’s accessing our APIs (typically through JWT), caching, usage quotes and rate limits.

Report/Report Job Controller

With our existing microservice architecture, we already had a good home for these new endpoints. In one of our existing APIs, we added a new Report and Report Job Controller that would be responsible for:

  • Standard CRUD operations.
  • Generating the report.

The Report Job’s generate report endpoint would ultimately handle sending requests via Flurl to a new Azure Function to generate the Report.

Reporting with Azure Functions

The Azure Function would be the brains of the operation. Accessed via an HttpTrigger, we would POST a configuration model, and the Function would operate asynchronously. From there, the Function would need to communicate with any number of APIs to aggregate data, then generate and email the report.

To be able to email the resulting report, we can take advantage of Twilio’s SendGrid integration Azure Functions and Azure have.

So why did this approach work well for us?

How It Fits In

From diagraming how this feature could be built, you can see quickly how well this approach satisfied the business requirements and would seamlessly fit our current architecture.

  • Users had a dedicated area to save and run reports and view the state of those reports.
  • Users would not be sitting there waiting for the request to be completed since the Azure Function was running the report generation asynchronously. This also ensured the Azure Function didn’t timeout since it used an HttpTrigger.
  • We leveraged an existing API and Database.
    • The advantage here is that we didn’t introduce new services that would require extra ARM template management or CI/CD management.
  • We leverage an Azure Function to be the reporting brains.
    • Can hook our APIs in.
    • Can create strategies to generate different reports, allowing flexibility for enhancements in the future.
    • Can take advantage of SendGrind integration to email the reports.
    • Can create an ARM template to handle creating/updating the Function in a target environment.

With the plan laid out, let’s take a look at how easy it was to integrate SendGrid with the Reporting Azure Function.

SendGrid Integration

We include the SendGrid binding in our Azure Function to begin the integration.

 [FunctionName("CreateSystemReport")]
        public static async Task<IActionResult> Run(
            [HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = null)] HttpRequest req,
            [SendGrid] IAsyncCollector<SendGridMessage> messageCollector,
            ILogger log, ExecutionContext executionContext)
        {
            log.LogInformation("Request for Report generation received.");

            string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
            log.LogDebug($"Event Message: {requestBody}");
            
            var context = new ReportProcessorContext(requestBody, messageCollector, log);
            context.ProcessRequest();

            return new NoContentResult();
              
        }

The SendGrid binding does need one piece of information to connect properly, and that’s its API key. One way to do that is by specifying the ApiKey name as a property in the binding.

[SendGrid(ApiKey = "CustomSendGridKeyAppSettingName")] IAsyncCollector&lt;SendGridMessage> messageCollector

Alternatively, you can omit the ApiKey, which will default to looking in your settings file for a property named AzureWebJobsSendGridApiKey.

So, where do you get this key?

Setting up SendGrid in Azure

To acquire your API key for SendGrid, you must first create a SendGrid user account in Azure. This is a very straightforward process and is effectively a sign-up form.

When you’ve completed creating the account, you’ll see that there’s now a new service with a type of SendGrid Account.

Clicking on it will take you to the service overview page in Azure where you can click the Manage button to access the SendGrid portal.

Twilio SendGrid dashboard

SendGrid will walk you through creating an API key and testing it, but in case that isn’t the experience you’re presented with, you can create the keys yourself. Under Settings in the navigation menu, you’ll see an API Keys item.

Twilio SendGrid API Keys

This will list any keys you’ve created so far. For me, I’ve already created a key so you see my existing development one listed. If you click the Create API Key button in the top right, you’ll be presented with a very simple form.

Twilio SendGrid Create API Key

After you fill out the form, you’ll be presented with your API Key. This is the key you can use to integrate your Azure Function with SendGrid.

Sending Emails with Attachments

Now that we have an API Key and we’ve connected SendGrid to our Azure Function, we can send emails. In our case, we want to send an email with an attachment, so let’s see how we did just that.

If you remember from our example above, our binding brought into the Function the IAsyncCollector<SendGridMessage> messageCollector.

 [SendGrid] IAsyncCollector<SendGridMessage> messageCollector

To be able to send an actual email, we just need to create a SendGridMessage object, pass it to your messageCollector and off it goes.

//Create message
var message = new SendGridMessage();
message.AddTo(_request.Recipient);
message.AddContent("text/html", "The Work Load report is attached.");
message.SetFrom(new EmailAddress("notreal@fake.email");
message.SetSubject(baseName);

//Send the message
messageCollector.AddAsync(message);

Super easy. So, how do we add an attachment to the message? Ultimately, we need to send the report along with the email. To do that, we create an appropriately named Attachment object that is handed to the message.

//Create the Workbook of the Report
....

//Save it to a stream  
MemoryStream ms = new MemoryStream();
workBook.SaveAs(ms);

//Create the attachment                    
var reportAttachment = new Attachment()
{
   Content = Convert.ToBase64String(ms.ToArray()),
   Type = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
   Filename = "Report.xlsx",
   Disposition = "inline",
   ContentId = "Report"
};
                  
//Create message
var message = new SendGridMessage();
message.AddTo(_request.Recipient);
message.AddContent("text/html", "The Work Load report is attached.");
message.SetFrom(new EmailAddress("notreal@fake.email");
message.SetSubject(baseName);

//Add the attachment to the message
message.AddAttachments(new List<Attachment>() { reportAttachment });

//Send the message
messageCollector.AddAsync(message);

Once again, super simple.

Conclusion

It was amazing to me how simple the integration between Azure Functions and SendGrid turned out to be. The integration’s simplicity allowed me to focus on more important things related to business needs vs. an extensive implementation. Not to mention, SendGrid is free for up to 25,000 emails and scales for cheap beyond that.

I’m a happy developer with how this turned out, given the requirements and limitations. As always, I’d love to hear about your experiences with Azure Functions, SendGrid, or just technology in general.

About Rōnin Consulting – Rōnin Consulting provides software engineering and systems integration services for healthcare, financial services, distribution, technology, and other business lines. Services include custom software development and architecture, cloud and hybrid implementations, business analysis, data analysis, and project management for a range of clients from the Fortune 500 to rapidly evolving startups. For more information, please contact us today.

]]>
Angular Resolvers: How To Master Them https://www.ronin.consulting/front-end/angular-resolvers-and-when-to-use-them/ Wed, 08 Apr 2020 23:37:47 +0000 http://www.ronin.consulting/?p=559 When working with Angular resolvers and Components, you’ve probably come across situations where you load the data a Component needs when it loads. Though there’s nothing wrong with that approach, you may find yourself with a lot of repetitive code depending upon how your APIs are structured.

For example, let’s say Acme Inc. has many multiple locations across the world; which we will call Branches. Your job is to build a SPA for them and in that SPA you have:

  • @Component() SuperAwesomeView
    • Needs a listing of Branches
  • @Component() AnotherSuperAwesomeView
    • Needs a listing of Branches

So naturally you may write some Component code that looks like this:

@Component({
    selector: 'app-super-awesome-view',
    templateUrl: './super-awesome-view.component.html',
    styleUrls: ['./super-awesome-view.scss']
})
export class SuperAwesomeViewComponent implements OnInit {
    branches$: Observable<Branch[]>;

    constructor(private branchService: BranchService) {}

    ngOnInit(): void {
        this.branches$ = this.branchService.getAll();
    }
}

Pretty simplistic right? The problem that we introduce with this approach is really around request management in Components. Let’s say these Components require other fairly generic resource information.

export class SuperAwesomeViewComponent implements OnInit {
    branches$: Observable<Branch[]>;
    users$: Observable<User[]>;
    providers$: Observable<Providers[]>;

    constructor(private branchService: BranchService,
private userService: UserService,
private providerService: ProviderService) {}

    ngOnInit(): void {
        this.branches$ = this.branchService.getAll();
        this.users$ = this.userService.getAll();
        this.providers$ = this.providerService.getAll();
    }
}

Now our Component has grown pretty quickly, we’ve introduced more variables, Injectables, and requests. Though 100% necessary for Components that need this data, there is a better way to manage and share these resource requests. Enter the Resolve Interface in Angular, which its concrete implementations are commonly referred to as Resolvers.

Resolvers

Angular resolvers are effectively an @Injectable that implements the Resolve Interface with the goal of returning some data model.

interface Resolve<T> {
  resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<T> | Promise<T> | T
}

Though at first glance, you may think “well I was already returning some data in my Component, how is this better?”, the power of the Resolver really comes into play with how Angular uses it.

A Resolver can be added to any Route that you define in your Routing Module, allowing the requests to process during the Router’s navigation lifecycle before your Component is loaded. Some benefits to this approach:

  • When the Component loads, data is preloaded.
  • Component code is more meaningful and less cluttered.
    • More logic geared towards the Components true purpose.
    • Less management around handling loading indicators etc.
  • Code is centralized and modular.
  • Hooking into the Router provides Resolver related events we can listen for.

So let’s explore this a bit and see how we can implement and apply a Resolver, access its data in a Component, and finally provide an indication to the user during navigation that content is loading.

import { Injectable } from '@angular/core';
import { Resolve, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';
import { Observable } from 'rxjs';
import { BranchService } from '@app/services';
import { Branch } from '@app/models';

@Injectable({ providedIn: 'root' })
export class BranchResolver implements Resolve<Observable<Branch[]>> {
    constructor(private branchService: BranchService) {}
    resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<Branch[]> {
        return this.branchService.getAll();
    }
}

See, easy! In the above example, we marked the class with @Injectable and made it available to the application through its providedIn config property. From there, we implement the Resolve<T> Interface and type it properly; which for us is Resolve<Observable<Branch[]>>. Then implement the resolve function, handing it a reference to the API call we want to make.

Hooking our Angular Resolvers Up

With our Resolver implemented, we can use it in our application. To do so, we need to apply it to any route that needs it. In your Routes array, find the Route that you’d like to apply it do. Within that Route, in addition to the properties like path and component, add one called resolve. This property will take an Object with a property we define and map to the Resolver.

{
   "path":"awesome",
   "component":"SuperAwesomeViewComponent",
   "resolve":{
      "branches":"BranchResolver"
   }
}

Once again, it’s pretty simple right? We’ve quickly built a Resolver and now applied it to our Route. So how do we access the data in our Component?

angular

Accessing Resolver Data

If we hop back to our SuperAwesomeViewComponent example, we will need to amend it a bit.

 export class SuperAwesomeViewComponent implements OnInit {
    branches$: Observable<Branch[]>;

    constructor(private activatedRoute: ActivatedRoute) {}

    ngOnInit(): void {
       const data:Data = this.activatedRoute.snapshot.data;
       this.branches$.next(data.branches);   
    }
}

We’ve swapped out the BranchService for the Angular ActivatedRoute. The ActivatedRoute allows us to access all resolved data and use it in our Component. If you remember that in the Route we added our Resolver to we defined an Object with the data we want to use, the Data object we receive from the ActivatedRoute is that Object.

Router Events and Loading Screen

With data being loaded during the navigation lifecycle and being handed to our Component, we now want to show some indication to the user that we’re loading data. To do this properly, we’ve going to move a bit higher up in the Component tree to the AppComponent. In your SPA, you probably have a root Component like the AppComponent that houses the root router-outlet. This Component will be where we centralize listening for Resolver-based events in the Router and overlaying a loading screen. For example:

export class AppComponent implements OnInit {
  loading: boolean = false;
  constructor(private router: Router) {}

  ngOnInit(): void {
    this.router.events
      .pipe(
        filter(
          event => event instanceof ResolveStart || event instanceof ResolveEnd
        )
      )
      .subscribe((event: RouterEvent) => {
        if (event instanceof ResolveStart) {
          this.loading = true;
        } else if (event instanceof ResolveEnd) {
          this.loading = false;
        }
      });
  }
}

The Router emits two events aimed at the Resolvers, ResolveStart and ResolveEnd. They are pretty straightforward and tell us everything we need to know to show the loading screen. When we receive those events, we toggle a boolean flag to show a loading screen so the user is aware that we are fetching necessary data.

General Thoughts on Resolvers

The above walkthrough is pretty simple and really geared towards an understanding of how they can be used. It’s truly just another useful tool at your disposal when building an Angular application but it isn’t a blanket solution to all scenarios. Below are some thoughts I continually think about while building Angular applications and trying to structure requests properly.

How much can a Resolver Handle?

Resolvers are a great way to preload data, but they really depend on how your APIs are structured and what you expect in return. Are you calling a single API that’s returning a lightweight or heavy model? Or are you calling multiple APIs and performing transformations on the returned data? Regardless of which approach, what’s the user’s experience while it’s resolving? All of these questions are answered on a per project basis but should always be considered.

Error handling in a Resolver?

If we’re requesting data while navigating, you need to be smart about how you handle situations where an API is unreachable or there’s an exception in your API. Connectivity issues are common, whether they be directly to the API or to another service or database that API may use. Is it better to handle the user experience for this in the Component with the ability to reload or is a blanket error page good enough?

What kind of data should be retrieved?

Sure, simple resource data is a good candidate. It’s even better if that resource data tends to be static and can be cached with little overhead towards when to invalidate that cache. But what about things like tabular data?

Is a Resolver a good way to preload the first page of a table so when the Component loads it’s readily available? It could be. Depends on how generic you can make that code and if that code is truly needed in multiple places.

What about form data? I think Resolvers are an excellent way to pulling in data like saved forms and pre-populating those fields. Could it get hairy? Yeah, probably if your pages has a nasty form (which I’ve seen plenty), but it really comes down to how that form data is stored. Is it a single model or composed from different calls?

Conclusion

Resolvers are a super handy feature in Angular. They are simply built, reusable, and makes testing your code a bit simpler. Lastly, I’m honestly not sure most Angular developers are aware that this feature exists, so try implementing one on your project and it could make you look like an Angular guru! If you do, let me know how things went and if you have another angle to consider!

 

To learn more about Angular route Resolvers or more coding questions, contact our team at Rōnin today! 

About Rōnin Consulting – Rōnin Consulting provides software engineering and systems integration services for healthcare, financial services, distribution, technology, and other business lines. Services include custom software development and architecture, cloud and hybrid implementations, business analysis, data analysis, and project management for a range of clients from the Fortune 500 to rapidly evolving startups. For more information, please contact us today.

]]>
What’s in your Angular Bundle? https://www.ronin.consulting/front-end/whats-in-your-angular-bundle/ Wed, 04 Mar 2020 00:56:00 +0000 http://www.ronin.consulting/?p=512

If you’ve ever built an Angular Application before, keeping your build lean and quick is key. With a labyrinth of configuration options and an endless sea of third-party libraries to use, it’s very easy to increase the time it takes to build your application if left unchecked.  Even more so if you work on a large team where developers work in parallel.  So how can we avoid this potential pitfall so your build remains performant?  Below, we will discuss on a particular tool we use to simplify our process: the web pack Angular bundle analyzer tool. 

How to Use the Angular Bundle Tool 

Consider using the web pack Angular bundle analyzer tool to simplify your process.  This tool takes in a stats file generated from the Angular CLI and provides an interactive FoamTree.  The benefit here is quickly identifying problem areas where unnecessary files may be included in your application’s distribution.  Let’s take a look at how to do this. 

Step 1

First, we must install the webpack-bundle-analyzer package and save it to our package.json.  We can do this with the below command.

npm install webpack-bundle-analyze --save-dev

Step 2

Now that the analyzer tool is installed, we need to generate a build of our application with the stats file.  Run the below to generate a production build with the stats file.

ng build --prod --stats-json
BundleAnalyzerOutput
Example Output

Step 3

With the generated build, you can check your dist folder and see the generated stats.json file that was created.  That’s the file we will hand to theangular bundle analyzer to visualize for us.

npx webpack-bundle-analyzer dist/stats.json
BundleAnalyzerFoamTree

This will boot up a local server on port 8888 with our FoamTree.  When you hit the site it may feel a bit overwhelming with how many files are included in your bundle, but you’ll certainly find some weasels in there that could give your application a little boost.

 

It’s that easy, folks! More Angular tips and tricks to come; stay tuned or reach out to a Rōnin for any and all questions!

About Rōnin Consulting – Rōnin Consulting provides software engineering and systems integration services for healthcare, financial services, distribution, technology, and other business lines. Services include custom software development and architecture, cloud and hybrid implementations, business analysis, data analysis, and project management for a range of clients from the Fortune 500 to rapidly evolving startups. For more information, please contact us today.

]]>