Speaker 1: So have you ever stopped to think about command line interfaces,
Speaker 1: even if maybe you didn't quite realize you were using one.
Speaker 2: Yeah, I mean think about it every time you type
Speaker 2: get commit right.
Speaker 1: Right, or launch a Docker container.
Speaker 2: Exactly, or even when your computer updates using things like
Speaker 2: apped on Linux or brew on macOS. That's all CLI
Speaker 2: and get this.
Speaker 1: Even huge graphical apps like say chat GPT often have
Speaker 1: these powerful CLI tools working behind the scenes for automation
Speaker 1: advanced control.
Speaker 2: It's pretty amazing, isn't it. They really are everywhere, They
Speaker 2: truly are. It's fascinating. Really, these tools are just incredibly
Speaker 2: omnipresent across well the whole IT landscape. And it's not
Speaker 2: just developers using the dot net cli or no jscli,
Speaker 2: you know, DevOps engineers they rely on them constantly. GitHub
Speaker 2: cli as your debub cli.
Speaker 1: Oh yeah.
Speaker 2: System admins manage whole infrastructures with PowerShell, Bash, package managers
Speaker 2: like Choco or wing it on Windows right, and even
Speaker 2: data scientists are in on it. Scripting with Python are
Speaker 2: Panda's sequel. They really do underpin so much of our
Speaker 2: digital world. Kind of like efficient backbones.
Speaker 1: Efficient backbones. I like that, So welcome everyone to the
Speaker 1: deep dive.
Speaker 2: Hello.
Speaker 1: Today we're taking a bit of a shortcut to getting
Speaker 1: really well informed on a topic that Okay, I might
Speaker 1: sound super technical, but it's actually incredibly practical, empowering even
Speaker 1: we're talking about building COLI applications with c sharp and
Speaker 1: dot Net, and we're drawing some powerful insights from to
Speaker 1: Johnny Belloness, who is fantastic book on the subject.
Speaker 2: It's a great resource.
Speaker 1: Our mission today, well, it's to unpack the core ideas,
Speaker 1: some maybe unexpected benefits and the sort of step by
Speaker 1: step process for creating these cross platform command line apps
Speaker 1: and more importantly, why you listening right now should absolutely
Speaker 1: care about.
Speaker 2: This stuff into Johnny Belminsur is really an exceptional guide
Speaker 2: for this. I mean over twenty one years of experience,
Speaker 2: especially in dot net and Azure. Wow. Yeah, and apparently
Speaker 2: he wrote his first program in quick basic when he was.
Speaker 1: Just eight eight Okay, that's impressive, right.
Speaker 2: So that extensive hands on background it makes them uniquely
Speaker 2: qualified to explain these sometimes complex topics. And what's interesting
Speaker 2: is how he uses cutting edge AI to like enhance
Speaker 2: the language and clarity in his writing. Oh interesting, but
Speaker 2: he ensures the actual content integrity is strictly his own.
Speaker 2: It's a neat blend.
Speaker 1: That is a neat blend. So okay, what does this
Speaker 1: all mean for you, our listener? Why should you care
Speaker 1: about CLI apps?
Speaker 2: Well, they aren't just obscure tools for you know, hardcore coders.
Speaker 2: They are seriously powerful productivity enhancers. They can drastically simplify
Speaker 2: your life. Also, well, for one, they help you stay
Speaker 2: focused less context switching. Right, you're not jumping between a
Speaker 2: dozen different graphical windows all the time.
Speaker 1: Okay, yeah, I feel that pain.
Speaker 2: And they automate those repetitive, error prone tasks that just
Speaker 2: eat up your time. And you know, they can even
Speaker 2: build things like workstation profiles.
Speaker 1: Workstation profiles, tell me more about that.
Speaker 2: Imagine this, Instead of spending like hours annually installing and
Speaker 2: configuring software for a new team member or even just
Speaker 2: a fresh PC, yeah the worst, a single command could
Speaker 2: potentially automate the entire setup. Curiously, Yeah, think about the
Speaker 2: time and frustration saved, Whether it's just for your personal
Speaker 2: workflow or for a whole organization. It's huge.
Speaker 1: That is huge. So this deep dive, it's kind of
Speaker 1: offering those aha moments, right, giving you a clear path
Speaker 1: to understanding the how and the why these tools are so.
Speaker 2: Transformative exactly a shortcut to that understanding.
Speaker 1: Okay, let's unpack this first bit, then the unsung hero.
Speaker 1: Why are cli apps everywhere? And what are they really
Speaker 1: built on? We've touched on how widespread.
Speaker 2: They are, Yeah, they're ubiquity, but it's worth.
Speaker 1: Hammering home just how ingrained they are, often working silently,
Speaker 1: making things stick.
Speaker 2: And connecting this back to productivity. Yeah, that's where they
Speaker 2: really shine, keeping.
Speaker 1: You focus way less context switching exactly.
Speaker 2: Every time you switch apps, email, project, tool chat, the
Speaker 2: chance of getting to rail just skyrockets, you know, totally.
Speaker 2: Celi tools minimize that you string together complex operations with
Speaker 2: simple commands and those workstation profiles. Traditionally that meant custom
Speaker 2: OS images.
Speaker 1: Oh, the big clunky images.
Speaker 2: Yeah, costly storage, OS updates, tool updates, human frustration. It
Speaker 2: was a mess, I can imagine. But by using CLI
Speaker 2: apps like PowerShell, scripts with package managers like Chocolatey or Wingt,
Speaker 2: it can automate the whole setup based on user rolls.
Speaker 2: Users get more autonomy, Manual installed time plummets. It's a
Speaker 2: shift from manual, air prone work to automated, repeatable.
Speaker 1: Processes that sounds like a massive win. I can just
Speaker 1: picture the IT department breathing a collective sigh of relief. Absolutely,
Speaker 1: And what's really fascinating is you said, at the heart
Speaker 1: of all this sophistication, it's basically just a simple console
Speaker 1: application pretty much.
Speaker 2: That's the surprising truth. It provides that basic structure before
Speaker 2: you start adding all the CLI bills and whistles.
Speaker 1: So if you want to build these, what are the
Speaker 1: essential tools? What's the starter kit?
Speaker 2: Okay, your developer toolkit Visual studio Code is a huge one.
Speaker 2: It's free, cross platform, super powerful, especially with all its extensions.
Speaker 1: Right, the extensions make it.
Speaker 2: Then you need the dot net SDK. That's crucial for
Speaker 2: c shark development, the foundation for building and running dot
Speaker 2: net apps. Got it and Get absolutely indispensable for version control,
Speaker 2: tracking changes, collaborating. You need GET.
Speaker 1: Can't live without Get any key vus code extensions you'd
Speaker 1: recommend definitely.
Speaker 2: The c sharp DevKit is essential now in telecode for
Speaker 2: C sharp DevKit. That gives you that AI powered whole
Speaker 2: line completion which is pretty.
Speaker 1: Slightly oh nice.
Speaker 2: And gitlans. It just makes working with get history inside
Speaker 2: vs codes so much easier. Boost productivity for sure.
Speaker 1: Okay, so we start with this basic console application. How
Speaker 1: do we level it up? How do we go from
Speaker 1: just simple text in text out to something that understands
Speaker 1: more complex commands, something user friendly.
Speaker 2: That's the next step, right, mastering user input beyond just
Speaker 2: that raw array of argument to.
Speaker 1: Get Yeah, the string arcs exactly.
Speaker 2: So system dot console is your starting point. Basic interactions
Speaker 2: right line and right for output, read line and read
Speaker 2: key for input.
Speaker 1: Standard stuff.
Speaker 2: Yeah, you can even use background color and foreground color
Speaker 2: for some visual feedback guide the user a bit. Oh,
Speaker 2: that's and and title is useful for labeling the console window.
Speaker 2: But just a heads up. It's only visible in like
Speaker 2: external terminals, not the integrated one in vs code.
Speaker 1: Usually good to know. But okay, what if your CLI
Speaker 1: app is doing something important, something long running, and the
Speaker 1: user just hits ctrol plus.
Speaker 2: C ah the panic button.
Speaker 1: Right, You can't just let it crash if it's saving
Speaker 1: data or cleaning up resources, can you?
Speaker 2: No? Absolutely not, Especially with sophisticated apps. You risk data loss,
Speaker 2: corrupted state. It's bad news. That's where the cancel keypress
Speaker 2: event is crucial.
Speaker 1: Cancel keypress it gets.
Speaker 2: Triggered by cutrol plus c or ctrl plus break and
Speaker 2: if you handle that event, your application gets a chance
Speaker 2: to do crucial clean before it exits, like what like
Speaker 2: saving at state, logging out of services, maybe closing database
Speaker 2: connections gracefully. It prevents those abrupt crashes and lost data,
Speaker 2: ensures a clean shutdown, vital for reliability.
Speaker 1: Okay, that makes sense for basic console apps. But real
Speaker 1: cli apps, the ones we use daily, they have named
Speaker 1: parameters right, and subcommands. It's much clearer than just trying
Speaker 1: to remember the order of arguments in a raw string
Speaker 1: array exactly.
Speaker 2: That's the big leap in user friendliness. Instead of my
Speaker 2: climb RG one ARC two and hoping you got the
Speaker 2: order right?
Speaker 1: Yeah, what was R two again?
Speaker 2: Right, you get a much more powerful discoverable interface named parameters, switches,
Speaker 2: subcommands like a bookmark, parklink, ad name my blog, you're lolo,
Speaker 2: hgtps dot my blog dot com.
Speaker 1: Ah Okay, much clearer, way clearer.
Speaker 2: And ps users understand what's expected, what the parameters mean,
Speaker 2: how to provide them correctly. Cuts down on guesswork and
Speaker 2: airs massively.
Speaker 1: So if we want to build these modern intuitive cli
Speaker 1: in dot net, what's the recommended library? Is there a
Speaker 1: go to that Microsoft pushes.
Speaker 2: Absolutely for dot net. System dot command line is the one.
Speaker 2: It's a robust dot Net Foundation project actively maintained by Microsoft.
Speaker 1: Okay, system dot command line. And why is it better
Speaker 1: than say, older ways of doing this, Well.
Speaker 2: The real insight is how it shifts your thinking. You
Speaker 2: move from manually parsing that messy command line input which
Speaker 2: is always a pain, to declaratively building intelligent interfaces. It
Speaker 2: uses this modern builder pattern. Think of it like defining
Speaker 2: your commands and options in a structured, readable way, building
Speaker 2: with Lego bricks, almost.
Speaker 1: Lego bricks for commands.
Speaker 2: I like it, yeah, And it's not just syntax. It
Speaker 2: makes your CLI inherently more robust, user friendly, and maintainable
Speaker 2: right from the start. It helps eliminate common input errors
Speaker 2: before they even happen.
Speaker 1: And it comes with built in goodies.
Speaker 2: Oh yeah, fantastic built in features. Command hierarchies you know
Speaker 2: like get has clone an ad automatic tab completion which
Speaker 2: users love. Even version number retrieval like version makes your
Speaker 2: CLI feel professional right out of the box.
Speaker 1: How does it work? Structurally?
Speaker 2: You define a root command that's the main entry point,
Speaker 2: then you add subcommands and these option t objects. The
Speaker 2: TA means they support various data types, strings, integers, booleans,
Speaker 2: file paths, you name it, plus aliases makes it super
Speaker 2: powerful and easy to use.
Speaker 1: That does sound powerful, but for someone maybe used to
Speaker 1: older libraries. What's the biggest mental shift with this builder pattern?
Speaker 1: Is it a steep learning curve?
Speaker 2: You know, it's surprisingly intuitive once you click with the concept.
Speaker 2: The main shift is going from imperatively grabbing stuff from
Speaker 2: ARGs and then manually checking.
Speaker 1: It right writing lots of if statements.
Speaker 2: To declaratively defining what your command expects. You basically tell
Speaker 2: system doc command like hey, I need a string here
Speaker 2: and it's required, or this is an integer it's optional,
Speaker 2: and it handles the parsing and the basic validation for you.
Speaker 1: Ah, so you focus more on the actual logic of
Speaker 1: the command.
Speaker 2: Exactly, not the tedious parsing plumbing.
Speaker 1: Excellent point. So building on that, how do we make
Speaker 1: sure the app is truly robust, especially with with user
Speaker 1: input and how do we manage data safely?
Speaker 2: Yeah, that leads right into proper input validation. You control
Speaker 2: the input using properties on those options like is required
Speaker 2: for mandatory ones, the user has to provide it, okay,
Speaker 2: makes sense? Or sometimes you want to accept multiple values
Speaker 2: for one option, right, like adding several bookmarks at once. Yeah,
Speaker 2: for that argumentarity one or more is super.
Speaker 1: Flexible argumentarity got it.
Speaker 2: And for really specific checks, say you want to assure
Speaker 2: a bookmark URL only points to like an internal corporate domain,
Speaker 2: you can use add validator to hook in your own
Speaker 2: custom validation logic. This way, bad data just doesn't even
Speaker 2: get into your application flow.
Speaker 1: That's smart, preventing problems early. What about dealing with files?
Speaker 1: Lots of CLI tools process files or output to them.
Speaker 1: How does the book approach that.
Speaker 2: That's a super common and critical task. Yeah. CLI apps
Speaker 2: often read from files or write.
Speaker 1: Results like import texport or processing logs exactly.
Speaker 2: So you can use file info options and combine those
Speaker 2: with validators like legal file names only just to make
Speaker 2: sure the file them is valid for the OS and existing.
Speaker 1: Only ah, to make sure the input file actually exists
Speaker 1: before you try reading it.
Speaker 2: Precisely. It prevents those nasty file not found exception errors downstream.
Speaker 2: This makes things like back up and restore features really robust,
Speaker 2: like the bookmark example in the book uses Jason importexport,
Speaker 2: or just sharing data between systems.
Speaker 1: Okay, so validation helps a lot, but Even with the
Speaker 1: best checks, things can still go wrong. Right, Software is complex?
Speaker 1: What's the safety net for unexpected errors? And how do
Speaker 1: we actually see what our app is doing? Especially if
Speaker 1: something does go wrong.
Speaker 2: You're absolutely right, No code is perfect. So error handling
Speaker 2: and CLI apps it follows standard best practices pretty much
Speaker 2: like any other app type.
Speaker 1: So try catch blocks, yep.
Speaker 2: Try cash finally is your friend, But the key is
Speaker 2: catching specific exceptions before the generic exception.
Speaker 1: Why is that.
Speaker 2: Important because it lets you give much better, more specific
Speaker 2: feedback to the user. Catching a file not found exception
Speaker 2: and saying, hey, that file doesn't exist is way more
Speaker 2: helpful than just a generic oops, an error occurred. Right.
Speaker 1: Users hate generic errors exactly.
Speaker 2: And creating your own custom exceptions like maybe a user
Speaker 2: already exists exception for the bookmark app makes your code
Speaker 2: clearer and lets you handle specific problem types cleanly.
Speaker 1: And beyond just reacting to.
Speaker 2: Errors, defensive programming is key, validating inputs up front, enforcing preconditions,
Speaker 2: basically trying to prevent errors before they even happen. That's
Speaker 2: your first line of defense.
Speaker 1: Okay, so error handling helps us recover, but how do
Speaker 1: we understand why errors happen? Or just track what the
Speaker 1: app is doing over time, especially in production.
Speaker 2: Ah. That's where structured logging is absolutely essential for diagnostics, monitoring, debugging, crucial.
Speaker 1: Stuff, and Jason is good for that.
Speaker 2: Jason is excellent. It's machine readable, lightweight, easily parsed by
Speaker 2: log management tools like Splunk or elastic stack.
Speaker 1: Makes sense. Any recommend library for dot net sera, Loog
Speaker 1: is fantastic.
Speaker 2: It's a really powerful dot net logging library that excels
Speaker 2: as structured logging.
Speaker 1: Structured logging what does that mean exactly?
Speaker 2: Instead of just logging a plain text message, you log
Speaker 2: events with rich typed data attached. So you might log
Speaker 2: an order placed event with properties like ordered customer, total amount.
Speaker 1: Ah okay, so you can easily search and filter later
Speaker 1: based on those properties.
Speaker 2: Precisely makes analyzing logs incredibly powerful. Sarah. Loog has various
Speaker 2: sinks destinations for your logs, console files, data bases, cloud services,
Speaker 2: tons of options and you can figure it yeah. Usually
Speaker 2: in appsettings dot Jason, you can set minimum log levels
Speaker 2: like only show warning and above and production to reduce noise.
Speaker 2: You can enrich logs with context like the machine name
Speaker 2: or thread id, which is super helpful for debugging tricky.
Speaker 1: Issues id yeah for concurrent stuff.
Speaker 2: Definitely, And one crucial thing always remember to call log
Speaker 2: dot close and flesh when your app exits. It ensures
Speaker 2: that any offered log messages are written out before the
Speaker 2: application shuts down completely. Guarantees you don't lose those last
Speaker 2: few vital log entries, especially if the app crashes.
Speaker 1: Good tip. Okay, so we've covered the nuts and bolts
Speaker 1: robustness error handling logging, but let's be real. Staring at
Speaker 1: a black screen with plain white text, it can be
Speaker 1: a bit well dry.
Speaker 2: Uh huh yeah, even for tech pros.
Speaker 1: So if we want users to actually enjoy using our
Speaker 1: CLI tools, maybe even love them, how do we go
Speaker 1: beyond just basic text? How do we make the experience
Speaker 1: interactive and dare I say, visually engaging?
Speaker 2: This is where a library like spector Console becomes an
Speaker 2: absolute game changer.
Speaker 1: Seriously, Specter Dot Console, okay, tell me more.
Speaker 2: It goes way beyond just simple color text. It lets
Speaker 2: you render really visually appealing elements right there in.
Speaker 1: The terminal, Like what kind of elements?
Speaker 2: Think ASKI art banners using figlet for a cool startup screen.
Speaker 2: Nice emojis to add a bit of personality, rich markup
Speaker 2: for bowlding, text specific colors, even clickable links in terminals
Speaker 2: that support it.
Speaker 1: Couple links in the terminal.
Speaker 2: Wow. Yeah, it's basically an enhanced alternative to the standard
Speaker 2: system dot console, better functionality, cross platform compatibility, and a
Speaker 2: whole suite of interactive components. Yeah. It turns that drab
Speaker 2: terminal into like a dynamic canvas.
Speaker 1: Okay, dynamic canvas.
Speaker 2: I like that.
Speaker 1: So it looks good, But how does it help with
Speaker 1: actual user interaction making it feel dynamic? Not just pretty?
Speaker 2: Great question? It excels there too. You can create dynamic
Speaker 2: experiences like selection prompts.
Speaker 1: Selection prompts yeah.
Speaker 2: Where the user sees a list of choices and can
Speaker 2: navigate using arrow keys and hit enter, much nicer than
Speaker 2: typing out an option number. Oh definitely, or live progress
Speaker 2: bars for long running operations. Gives the user real time
Speaker 2: feedback that something is actually happening.
Speaker 1: That's essential for longer tasks, avoids the is it frozen feeling.
Speaker 2: Exactly, and it's great for presenting complex data clearly using
Speaker 2: things like tree views.
Speaker 1: Tree views like for hierarchical data yep.
Speaker 2: Imagine organizing bookmarks by category in a nice tree structure
Speaker 2: right in the terminal output makes complex information much more
Speaker 2: digestible and honestly more engaging for the user. It really
Speaker 2: transforms that passive display into an active conversation.
Speaker 1: That sounds incredibly powerful for making CLIs more approachable, especially
Speaker 1: for maybe less technical users. Yeah, but is there a downside?
Speaker 1: Can too much interactivity be a bad thing?
Speaker 2: Sometimes you've hit on a really crucial point there. Balance
Speaker 2: interactivity is fantastic for human users, no doubt, but you
Speaker 2: have to use it wisely.
Speaker 1: Why is that?
Speaker 2: Think about automation. What if your CLI tool is also
Speaker 2: meant to be used in say a CICD pipeline.
Speaker 1: AH continuous integration, continuous delivery, automated builds and deployment.
Speaker 2: Right, those automated processes can't answer interactive prompts. An unexpected
Speaker 2: prompt would just hang the pipeline.
Speaker 1: Okay, so that's a problem. How do you handle that?
Speaker 2: You need to provide a non interactive mode. A common
Speaker 2: pattern is to add an interactive flag or option.
Speaker 1: So humans can run it with interactive to get the
Speaker 1: nice prompts in progress bars.
Speaker 2: Exactly, and the automated scripts run it without that flag,
Speaker 2: so it just executes non interactively. Maybe using default values
Speaker 2: or taking all input via parameters.
Speaker 1: That makes sense. Designed for different ways people and machines
Speaker 1: will use it.
Speaker 2: Flexibility precisely, it's about designing for different consumption patterns.
Speaker 1: Okay, So let's say our app is growing, We're adding features, interactivity,
Speaker 1: maybe talking to external services. How do we stop the
Speaker 1: code from turning into a giant, tangled mess. What are
Speaker 1: the foundations for scalable design, modularity integrating services cleanly?
Speaker 2: That's a critical concern as apps evolve. That program dot
Speaker 2: cs file in the book's example, it apparently swelled from
Speaker 2: one hundred and ninety one lines to four hundred and
Speaker 2: seventy nine.
Speaker 1: Wow. Yeah, that gets on wieldy fast.
Speaker 2: So refactoring fromodularity becomes essential. One useful technique mentioned is
Speaker 2: using a code map, a visualization tool to see the
Speaker 2: relationships between different parts of your code. Helps you spot complexity.
Speaker 1: A codemap interesting.
Speaker 2: The goal is a logical project structure, maybe separate folders
Speaker 2: for commands and services. This improves readability, makes it easier
Speaker 2: to maintain, and crucially, it makes it easier to add
Speaker 2: new features without breaking existing ones and easier for new
Speaker 2: developers to onboard.
Speaker 1: Makes sense and are there core software design principles that
Speaker 1: guide this?
Speaker 2: Absolutely? The dependency inversion principle or DIP is key here.
Speaker 2: It's one of the solid principles fundamental ideas for building maintainable, scalable.
Speaker 1: Software dependency inversions. Fancy, what's the core idea?
Speaker 2: It basically says that high level parts of your system
Speaker 2: shouldn't depend directly on low level concrete implementations. They should
Speaker 2: depend on abstractions, usually interfaces.
Speaker 1: Okay, can you give an analogy.
Speaker 2: Maybe think of your car steering wheel. It's not welded
Speaker 2: directly to the wheels, right now, that'd be weird. There's
Speaker 2: an interface that the steering column, the linkage, a standardized connection.
Speaker 2: If you want to swap your regular tires for I
Speaker 2: don't know, monster truck tires, Okay, you change the tires,
Speaker 2: but the steering wheel and how it can generally stays
Speaker 2: the same. The interface decouples them.
Speaker 1: Ah. I see, So the steering wheel depends on the
Speaker 1: idea of steerable wheels, not one specific type of wheel exactly.
Speaker 2: In code, this means your command logic depends on an
Speaker 2: ibookmark service interface, not directly on the concrete bookmark service
Speaker 2: class that might talk to a specific database and the
Speaker 2: benefit decoupling. It makes the system way more flexible, easier
Speaker 2: to maintain. If you change how bookmarks are stored, maybe
Speaker 2: move from a file to a database, you only change
Speaker 2: the concrete bookmark service implementation. The commands using the ibookmark
Speaker 2: service interface don't need to change at all. It stops
Speaker 2: changes rippling through your whole codebase.
Speaker 1: That decoupling sounds incredibly valuable. So how does dot net
Speaker 1: actually help us implement this? Put DIP into action.
Speaker 2: Dot Net has excellent built in support for dependency injection
Speaker 2: or DEI is the technique that makes DIP practical.
Speaker 1: Okay, Dependency injection.
Speaker 2: Instead of your command code manually creating a new bookmark service,
Speaker 2: the DI container, managed by things like host builder or
Speaker 2: anservice collection and modern dot Net automatically provides an instance
Speaker 2: of the required service I bookmark service when the command
Speaker 2: needs it.
Speaker 1: So the framework handles creating and providing the dependencies.
Speaker 2: Right it injects them. This makes future changes much easier
Speaker 2: because you typically only need to register the service and
Speaker 2: its implementation in one central place your startup configuration. You
Speaker 2: don't have to hunt down every place the service is
Speaker 2: used and update it much cleaner, much easier to manage
Speaker 2: as the app grows.
Speaker 1: That sounds much better. What about reaching outside our CLI
Speaker 1: app using other systems or services.
Speaker 2: Yeah, consuming external APIs and services super common and powerful.
Speaker 2: Why reinvent the wheel right exactly? It lets you focus
Speaker 2: on your core logic and delegate other things, maybe payments,
Speaker 2: advanced logging, or like in the books, example, a separate
Speaker 2: bookmark synchronization service to specialized external systems. It leverages expert solutions.
Speaker 1: So how do we talk to those external services properly
Speaker 1: and securely? In dot Net? We need good performance, good
Speaker 1: resource management for talking.
Speaker 2: To external APIs. Dot Net's HTTP client is the tool,
Speaker 2: but crucially you should use IHTTP client.
Speaker 1: Factory HTP client factory. Why that Specifically?
Speaker 2: It manages the life cycle of HTTP client instances properly.
Speaker 2: Helps avoid common problems like socket exhaustion, which can happen
Speaker 2: if you create and dispose of too many HTP client
Speaker 2: instances directly. It pools and reuses them efficiently. Better performance,
Speaker 2: better resource management.
Speaker 1: Okay, good practice. Any architectural patterns recommended here?
Speaker 2: Yes, the service agent pattern is really insightful here. Think
Speaker 2: of it like a dedicated diplomat or translator for your app.
Speaker 2: When talking to a specific external service a service agent. Yeah,
Speaker 2: instead of your main CLI command logic making raw HTDP calls,
Speaker 2: figuring out URLs, header's authentication for some external API, which
Speaker 2: can get messy, it just talks to this specific agent class,
Speaker 2: like maybe a bookmark sinkur agent. It tells the agent, hey,
Speaker 2: sink these bookmarks. The agent then handles all the nitty
Speaker 2: gritty details of talking to the actual external bookmark sinker API,
Speaker 2: formatting the request, handling API quarks, retries, authentication, parsing the
Speaker 2: response AH.
Speaker 1: So it encapsulates all the communication logic for that specific
Speaker 1: external service exactly.
Speaker 2: It dramatically simplifies your core command logic, centralizes the API
Speaker 2: interaction details, and makes it much easier to change or
Speaker 2: update how you talk to that external service later without
Speaker 2: messing up your main application code.
Speaker 1: Reduces that tight coupling That service agent pattern sounds really clean,
Speaker 1: and it also leads us nicely into our next really
Speaker 1: critical area, quality assurance and security. How do we make
Speaker 1: sure this app we've built is reliable, trustworthy and safe
Speaker 1: for users?
Speaker 2: Absolutely crucial. Testing is your safety net? Simple as that
Speaker 2: your safety net. It ensures new features work obviously, but
Speaker 2: just as importantly it prevents regressions, you know, accidentally breaking
Speaker 2: something that used to work when you add a new
Speaker 2: feature or fix a bug.
Speaker 1: Yeah, regressions are the worst. So how do we approach
Speaker 1: testing systematic?
Speaker 2: The pyramid of testing is a good mental model. It
Speaker 2: outlines different levels.
Speaker 1: Okay, what are the levels?
Speaker 2: At the broad base, you have unit tests small fast
Speaker 2: tests verifying individual methods or functions in isolation, often using mocking,
Speaker 2: which we should talk about unit tests first. Then integration tests.
Speaker 2: These check that different components work together correctly, like does
Speaker 2: your service layer talk to the database properly?
Speaker 1: Got it?
Speaker 2: Above that? System tests These are end to end tests
Speaker 2: verifying the entire applications functionality from the user's perspective, Does
Speaker 2: the bookmark ad command actually add a bookmark?
Speaker 1: A whole flow?
Speaker 2: Right? And at the peak acceptance tests which confirm the
Speaker 2: app meets the actual user requirements. You also have functional
Speaker 2: tests what it does versus non functional tests? How well
Speaker 2: it does it? Like performance or security testing.
Speaker 1: That's a comprehensive pyramid for a practical approach, though, what
Speaker 1: should we really focus on testing? And maybe what shouldn't
Speaker 1: we waste time testing?
Speaker 2: Good question? Focus on testing the happy path everything works
Speaker 2: is expected, but also critically test the unhappy paths invalid
Speaker 2: input errors, user hitting sutrol plus.
Speaker 1: Two okay, both success and failure cases exactly.
Speaker 2: Test method behavior, check fortoon values, ensure exceptions are thrown
Speaker 2: and handled correctly. What not to test? Generally, don't test
Speaker 2: external frameworks themselves or simple data transfer objects DTOs those
Speaker 2: plain data structures. Their creators should have tested them. Focus
Speaker 2: on your logic and when?
Speaker 1: Do these different tests typically run in the development cycle.
Speaker 2: Unit tests run them frequently, like before every code commit.
Speaker 2: Ideally they should be fast. Integration tests usually run after
Speaker 2: unit tests pass. Maybe in your CI pipeline. Smoke tests
Speaker 2: are quick checks right after deployment to make sure the
Speaker 2: basics are working. System tests often run in a staging
Speaker 2: environment before the final production release.
Speaker 1: How do you set up testing in dot net?
Speaker 2: It's easy. Dot Net new ms test or x unit
Speaker 2: end unit creates a test project. Then you structure your
Speaker 2: test classes logically, maybe mirroring your application structure, like link
Speaker 2: command tests for the Bookmark apps link command.
Speaker 1: Okay, you mentioned mocking for unit tests. What's that about,
Speaker 1: especially when dealing with external things like databases or web
Speaker 1: services that can be slow or unreliable during tests.
Speaker 2: Right, This is where mocking is essential for effective unit testing.
Speaker 2: Think of it like using a stand in act or
Speaker 2: during a play rehearsal the stand in Yeah, libraries like
Speaker 2: n substitute or mock fake ad easy let you create
Speaker 2: fake controlled versions of your dependencies, like a fig ibookmark
Speaker 2: service that doesn't actually talk to a database, but just
Speaker 2: returns pre defined data or verifies that certain methods were called.
Speaker 1: Ah, So you can test your command logic without needing
Speaker 1: a real database or web service running exactly.
Speaker 2: It lets you test your method in complete isolation. It
Speaker 2: eliminates failures caused by external factors slow network service being down.
Speaker 2: If a unit test fails when using mocks, you know
Speaker 2: the bug is almost certainly in your code, not the
Speaker 2: dependency makes debugging way faster and moraliable.
Speaker 1: That makes a lot of sense for isolating tests. Now,
Speaker 1: shifting gears slightly but equally critical security. Is this something
Speaker 1: we just tack on at the end?
Speaker 2: Oh? Absolutely not. That's a recipe for disaster security is
Speaker 2: an afterthought. It has to be woven through the entire
Speaker 2: application life cycle.
Speaker 1: The entire life cycle from the very beginning.
Speaker 2: Ye yes. Starting with design, use threat modeling techniques like STRIDE.
Speaker 1: Or dread Stride dread.
Speaker 2: There are structured ways to brainstorm potential threats. STRIDE stands
Speaker 2: for spoofing, tampering, repudiation, information disclosure, denial of service, elevation
Speaker 2: of privilege. It helps you think like an attacker early on.
Speaker 1: Okay, thinking like an attacker during design. What else?
Speaker 2: Architecture using principles like defense and depth, securing communication channels, development,
Speaker 2: following secure coding practices, being aware of things like the
Speaker 2: OATS top ten common vulnerabilities, keeping your libraries and dependencies
Speaker 2: up to date.
Speaker 1: Right outdated libraries are a big risk, huge risk.
Speaker 2: Then testing penetration, testing, vulnerability assessments, deploy security ICD pipelines,
Speaker 2: managing secrets properly, no apikes, and source code definitely not
Speaker 2: ongoing usage, monitoring for suspicious activity, patching vulnerabilities promptly, and
Speaker 2: even logging being careful not the logs sensitive data or
Speaker 2: using tools like sarah log to sanitize it if you must.
Speaker 2: Security is everywhere.
Speaker 1: Wow, Okay, it really is a continuous process. Are there
Speaker 1: any specific tools, maybe simpler ones that developers can use
Speaker 1: to check the security of their dot net apps without
Speaker 1: being full blown security experts.
Speaker 2: Yeah, definitely, there are comprehensive tools like sonar, cub snack,
Speaker 2: mend bolt. But for a great starting point right within
Speaker 2: the dot net ecosystem, check out dot net audit.
Speaker 1: Dot net audit built in yep.
Speaker 2: You can run dot netlist package vulnerable. It checks your
Speaker 2: project's Newgit packages against the GitHub advisory database and tells
Speaker 2: you if you're using any versions with known vulnerabilities.
Speaker 1: Oh, that's super useful and easy it is.
Speaker 2: And dot netlist package outdated is also helpful if flags
Speaker 2: libraries that aren't necessarily vulnerable now but are old and
Speaker 2: might not be getting security patches anymore, prompting you to upgrade.
Speaker 2: Quick checks anyone can do.
Speaker 1: Fantastic. Okay, let's circle back to that bookmark singer external
Speaker 1: service example. How do we actually secure the communication between
Speaker 1: our CLI app and that service, especially if something like
Speaker 1: an apike or token is involved?
Speaker 2: Good question. Typically you'd implement authentication using something like a
Speaker 2: personal access token or pit a PET.
Speaker 1: Okay, how does that work?
Speaker 2: Your CLI app needs to send this PT to the
Speaker 2: external service with each request. A common way is via
Speaker 2: a custom HGTP header like xpat.
Speaker 1: So the token goes in the header securely hopefully over HTTPS.
Speaker 2: Absolutely acts is non negotiable. There The external service then
Speaker 2: receives a request, extracts a paid and validates to checks
Speaker 2: if it's known, if it's still valid, not expired, has
Speaker 2: the right permissions.
Speaker 1: And the CLI app needs to handle the responses like
Speaker 1: if the token is invalid exactly.
Speaker 2: The service might respond with a four to H one
Speaker 2: and off rised or four H three forbidden. Your cli
Speaker 2: needs to handle those responses gracefully, maybe prompt the user
Speaker 2: to re enter their token or update it.
Speaker 1: Where should the user store this pat? Not hard coded?
Speaker 2: Right, definitely not hard coded. A common approach is to
Speaker 2: store it in an environment variable like bookmark pat. The
Speaker 2: cli app can then read it from the environment when needed.
Speaker 2: Keeps the token out of source code and separate from
Speaker 2: the application deployment much more secure.
Speaker 1: Okay, environment variable for the pet. Got it? So I've
Speaker 1: built it, made it robust, interactive, modular, secure, tested it. Wow.
Speaker 1: Now the final hurdle. How do we actually get this
Speaker 1: amazing CLI app into the hands of our users. Let's
Speaker 1: talk packaging and deployment.
Speaker 2: Right the final mile. First, let's just clarify the terms
Speaker 2: because they sometimes get mixed up.
Speaker 1: Okay.
Speaker 2: Packaging is bundling everything needed to your app's code dependencies
Speaker 2: into a deployable unit. Distribution is making that package available
Speaker 2: like putting on our website or repository, and deployment is
Speaker 2: the actual act of installing and configuring it on the
Speaker 2: user's machine.
Speaker 1: Package, distribute, deploy Got it? So for our cross platform
Speaker 1: dot netcli app? What are the main options?
Speaker 2: You've got several good ones, and the best choice often
Speaker 2: depends on your target audience. Like well, first, you can
Speaker 2: package it as a dot net tool, a.
Speaker 1: Dot Net tool like dot net tool install exactly.
Speaker 2: You package your app as a standard nugat package using
Speaker 2: dot netpack, then publish it to NuGet dot org or
Speaker 2: a private feed. This is ideal for other developers or
Speaker 2: it pros who already have the dot Net SDK installed.
Speaker 2: They just run dot net tool installed global your tool name.
Speaker 2: Super easy for that audience.
Speaker 1: Okay, cool for devs.
Speaker 2: What else? Second option a dock or container. You write
Speaker 2: a Docker file that describes how to build an image
Speaker 2: containing your app and its runtime dependencies.
Speaker 1: Ah containerization yep.
Speaker 2: You build the image, Docker build, push it to Docker
Speaker 2: hub or another registry. Users then just need Docker Desktop
Speaker 2: or WSL installed and they can Docker pull your image
Speaker 2: and Docker run it. Great for portability, consistency across environments,
Speaker 2: and often a reduced footprint.
Speaker 1: Nice. Any options more geared towards say typical Windows users.
Speaker 2: Yes, Third option a wingt package. Wing it is the
Speaker 2: Windows package managed.
Speaker 1: Oh yeah, wing it install right.
Speaker 2: You create a manifest file basically metadata describing your app
Speaker 2: using the wind create cli tool. You typically use dot
Speaker 2: net published first to create a self contained executable that
Speaker 2: includes the dot Net run time so the user doesn't
Speaker 2: need dot Net installed separately. Then you submit your manifest
Speaker 2: to the official wing itt repository. Windows users can then
Speaker 2: just wing it install your app aid very seamless for them.
Speaker 1: Three solid options dot Net tool, Docker wing it. Once
Speaker 1: it's out there, though, how do we manage updates? Users
Speaker 1: need a way to get new versions fixed bugs?
Speaker 2: Versioning is crucial. The standard way is semantic versioning.
Speaker 1: Or sever major dot minor dot exactly.
Speaker 2: Major version bumps for breaking changes, minor for new features,
Speaker 2: backward compatible, and patch for bug fixes. It clearly communicates
Speaker 2: the nature of changes to users.
Speaker 1: How does that work with the different packaging methods for
Speaker 1: dot net tools.
Speaker 2: You update the version number in your projects dot cs
Speaker 2: pro file, run dot netpack and push the new nugat
Speaker 2: package version. Users can update with dot a tool update
Speaker 2: global or install specific versions for docer. You use tags
Speaker 2: when building your image doctor build, DASTMIAPP, dot one, dot one,
Speaker 2: bond zero. Users pull the specific tag they need. For
Speaker 2: wing it, you update the version in your manifest file
Speaker 2: and resubmit it. Wing It handles the upgrade process for
Speaker 2: users via wing it upgrade.
Speaker 1: Okay, version and handled. What about performance? We want our
Speaker 1: Coli app to be fast, especially in production. Do these
Speaker 1: packaging choices matter? And how do we optimize?
Speaker 2: Performance?
Speaker 1: Is?
Speaker 2: Definitely key and yes, packaging choices can influence it. First rule,
Speaker 2: always compile and release mode for production build plug debug mode,
Speaker 2: never debug mode for production. Release mode enables significant compiler
Speaker 2: optimizations that make your code run much faster and often
Speaker 2: use less memory.
Speaker 1: Okay, release mode? What else?
Speaker 2: For maximum efficiency, specially startup time, consider platform specific compilation.
Speaker 2: This tailor's the output binary for a specific OS and
Speaker 2: CPU architecture, or even go step further with ahead of
Speaker 2: time AOT compilation. AOT What's that it precompiles your dot
Speaker 2: net code directly to nat a machine code before deployment
Speaker 2: instead of relying on the just in time JIT compiler
Speaker 2: at run time. This can dramatically improve startup performance and
Speaker 2: reduce the app size and dependencies. Makes it feel incredibly snappy.
Speaker 1: Nice. The book had some examples too, right, like optimizing
Speaker 1: JSON serialization.
Speaker 2: Yeah, optimizing things like Jason serializer serialized ACNC by using
Speaker 2: filestream directly can improve ioperformance significantly compared to say, writing
Speaker 2: to a memory stream. First, little things add up.
Speaker 1: How do we find these performance bottlenecks in the first place?
Speaker 2: Profiling is key. Tools like benchmark dot net are fantastic
Speaker 2: for micro benchmarking specific pieces of code. It gives you
Speaker 2: precise metrics mean execution time, error margin, standard deviation for
Speaker 2: consistency allocated memory very detailed, helps you identify hotspots, methods
Speaker 2: that are called very frequently, hot spots, and hot paths
Speaker 2: the code execution paths taken most often target your optimization
Speaker 2: efforts there for the biggest impact. And for monitoring and production,
Speaker 2: tools like Azure Application Insights can give you real time
Speaker 2: performance data and help you spot issues as they happen.
Speaker 1: Okay, profiling and monitoring essential. So wrapping this all up.
Speaker 1: What does this journey mean for you, our listener.
Speaker 2: Well, we've gone from understanding what CLI apps even.
Speaker 1: Are, right, they're surprising ubiquity.
Speaker 2: Through building them with powerful dot Net tools like system
Speaker 2: dot command line, making them interactive and user friendly with
Speaker 2: Spectro dot.
Speaker 1: Console, making them robust with validation, air handling logging.
Speaker 2: Designing them well using modularity, DIPDI, integrating external services.
Speaker 1: Safely, securing them, testing them thoroughly.
Speaker 2: Optimizing for performance, and finally packaging and deploying them using
Speaker 2: various methods like dot net tools, Docker or wing it.
Speaker 1: It's quite a journey, it really is, and it shows
Speaker 1: that these CLI applications, even without a fancy graphical interface,
Speaker 1: are incredibly powerful tools for automation for products.
Speaker 2: Absolutely, they streamline workflows, solve real problems efficiently. Don't underestimate
Speaker 2: the power of the command line.
Speaker 1: So here's a final thought, maybe a provocative one for
Speaker 1: you to chew on. Think about that bookmark application or
Speaker 1: just your own daily computer. Use those simple tasks you
Speaker 1: do over and over with a mouse clicking through menus?
Speaker 1: What if you could automate them? What comment maybe even
Speaker 1: slightly annoying repetitive tasks in your workflow, personal or professional
Speaker 1: could be dramatically sped up if you built a small,
Speaker 1: simple CLI tool for them.
Speaker 2: Yeah, think about the hidden value there. Saving clicks, sure,
Speaker 2: but also saving context switches, reducing cognitive load. A simple
Speaker 2: command could bring surprising benefits to.
Speaker 1: Your day to day exactly. So we really encourage you
Speaker 1: to explore the resources we've mentioned, especially to Johnny Bilminser's
Speaker 1: book building CLI applications with c sharp and dot net,
Speaker 1: and maybe check out libraries like system dot command line
Speaker 1: and Spectro dot console.
Speaker 2: Because you know, at the end of the day, as
Speaker 2: the book wisely points out, your users aren't really interested
Speaker 2: in your amazing coding skills or how elegantyourprogram dot cs.
Speaker 1: Is huhuh, probably not.
Speaker 2: What they're interested in is the value your application, your
Speaker 2: CLI application brings to them. Does it solve their problem?
Speaker 2: Does it make their life easier?
Speaker 1: That's the bottom line. So if this deep dive has
Speaker 1: sparked an idea, however small, grab those resources, start experimenting.
Speaker 1: You might surprise yourself with the power you can unlock
Speaker 1: with CLI tools in your own world.
Speaker 2: It's all about the value you create.
Speaker 1: Couldn't agree more