Layered API Security with Ted Spence - .NET 143
Ted Spence is the Head of Engineering at ProjectManager. He joins the show with Mark and Shawn to talk about "Layered security for DotNet Core APIs". He talks about his experience in building and testing APIs.
Hosted by:
Shawn Clabough
Special Guests:
Ted Spence
Show Notes
Ted Spence is the Head of Engineering at ProjectManager. He joins the show with Mark and Shawn to talk about "Layered security for DotNet Core APIs". He talks about his experience in building and testing APIs.
Sponsors
Links
Socials
Picks
Transcript
Shawn_Clabough:
Hello and welcome to another episode of Adventures in.NET. I'm Sean Cable, your host. And with me today, your co-host Mark Miller. Hey Mark.
Mark_Miller:
Sean!
Shawn_Clabough:
Hey.
Mark_Miller:
How you doing, buddy?
Shawn_Clabough:
Good, good.
Mark_Miller:
I'm a pro now, man!
Shawn_Clabough:
I heard,
Mark_Miller:
I've done
Shawn_Clabough:
I
Mark_Miller:
it
Shawn_Clabough:
heard.
Mark_Miller:
for a full show already! And I'm still back! You haven't figured out how to mute me yet, I think.
Shawn_Clabough:
Well, I can do that right now, you know,
Mark_Miller:
Uh
Shawn_Clabough:
but
Mark_Miller:
oh, okay.
Shawn_Clabough:
no, I'll give you a chance. Yeah.
Mark_Miller:
Alright,
Shawn_Clabough:
Yeah.
Mark_Miller:
give me another
Shawn_Clabough:
Yeah.
Mark_Miller:
chance!
Shawn_Clabough:
Well,
Mark_Miller:
That's all I asked,
Shawn_Clabough:
you
Mark_Miller:
Sean!
Shawn_Clabough:
haven't used all your streaks up, so
Mark_Miller:
Just
Shawn_Clabough:
yeah,
Mark_Miller:
one...
Shawn_Clabough:
you're good. We'll
Mark_Miller:
One
Shawn_Clabough:
keep
Mark_Miller:
more
Shawn_Clabough:
you.
Mark_Miller:
episode!
Shawn_Clabough:
Okay. Um,
Mark_Miller:
Please!
Shawn_Clabough:
let's welcome our guests. So let's bring on Ted Spence. Hey Ted.
Ted_Spence:
Hey, thanks for having me. Love the energy you guys have already.
Mark_Miller:
Yeah.
Shawn_Clabough:
Yeah, Mark is well known for his energy.
Ted_Spence:
There we are. Well, I just got off of giving a talk at Bellevue College about renewable energy, so it's a fun world to be in.
Shawn_Clabough:
Bellevue College and where?
Ted_Spence:
at Bellevue College in Washington. We had Earth Week this week, so we talked about a lot of things related to climate, and I teach data analytics there, so I talked about data about renewable energy. It was a ton of fun, and clearly Mark's got renewable energy in droves.
Shawn_Clabough:
Very nice, very nice. Yeah, I am from a small town. I guess it's not so small anymore, just south of there. So I'm from Puyallup. So,
Ted_Spence:
Brilliant.
Shawn_Clabough:
but I live
Mark_Miller:
Poo.
Shawn_Clabough:
in Idaho now, so.
Mark_Miller:
I think it's pronounced poo-ee-a-loop. Pretty sure about that, Sean. You better check.
Shawn_Clabough:
Have you been there Mark?
Mark_Miller:
Not to Bewellup, but I did actually grew up in Bellevue. I went to school in Bellevue as a kid. So yeah, I walked around there, you know, before it all grew. I was there when it was a much smaller kind of town. But yeah, it's pretty cool.
Shawn_Clabough:
So
Ted_Spence:
Fantastic.
Shawn_Clabough:
you had to,
Ted_Spence:
My daughter loves Puyallup Fair.
Shawn_Clabough:
yeah,
Ted_Spence:
It's
Shawn_Clabough:
I was gonna
Ted_Spence:
one of her
Mark_Miller:
Alright.
Shawn_Clabough:
say,
Ted_Spence:
favorite
Shawn_Clabough:
you had
Ted_Spence:
things
Shawn_Clabough:
to bend
Ted_Spence:
every
Shawn_Clabough:
to
Ted_Spence:
year.
Shawn_Clabough:
the fair mark.
Mark_Miller:
Yeah, yeah, yeah, the fair. Well, I've been to the fair. I have a couple times. So, yeah, I have been there.
Shawn_Clabough:
All right, cool. All right, Ted, so we're going to talk today about everybody's favorite topic, security.
Ted_Spence:
Yeah, security is just one of those things that if you're not talking about it, you really should be because somebody is probably attacking your product as we speak. It's really critical and you have to worry about it. And I have a little story if you don't mind me leading off with.
Shawn_Clabough:
Yeah, go ahead.
Ted_Spence:
So this was quite a few years ago. It was probably six or seven years ago. I was building an API for a really large company, Avalara. They do taxes. and they wanted a new API. So we held an internal hackathon to teach people about this new API that we were about to launch. And halfway through the hackathon, somebody raised their hands. We were all in a meeting room together and somebody raised their hand like, oh my gosh, I found something. They had found a rarely used API and they found that if they used it and they guessed a random number, they were able to fetch their neighbor's data. And we were like, oh my gosh, you know, what's going on here? Well, clearly we were storing all the data in the same database. And this person had figured out an attack to bypass our security protections. So
Mark_Miller:
Yeah,
Ted_Spence:
I bet you're
Mark_Miller:
that's...
Ted_Spence:
gonna ask me how we solved it.
Mark_Miller:
That's...
Shawn_Clabough:
Hehehe
Mark_Miller:
I just want to say that's not a good... That's not good. I mean, it's good that you found it.
Ted_Spence:
That's the thing, that's the point of hackathons, is we wanted to be extremely careful and extremely prepared, and because of it we were prepared. But what we did is we reacted appropriately. Rather than just fixing that one bug, we said, wait a minute, this particular problem is extremely serious. It should never have gotten to this point. How can we redefine our system so it is fundamentally not possible for this to ever happen again?
Mark_Miller:
Yeah.
Ted_Spence:
And that's really the right approach, is people can tell you you have a security problem, but it's how you react to the security problem that defines your security levels.
Shawn_Clabough:
So what about those people that, you know, they're going, I don't need to do security, whatever's in my system, it's all public, you know, out to a website or whatever there, I don't need to worry about security. You know, if they break in, they break in, you know, big deal.
Ted_Spence:
Well, you know, there's a lot of sites that are just built that way. And I'll tell you, those are the sites that don't last. If you build a site and you don't spend time thinking about security, sooner or later, all sorts of bad things will happen. You know, I was a.NET developer early on. Prior to that, I wrote code in all sorts of other systems, including CGI binaries in the 90s. And no matter what we built, people would find ways to break it sooner or later. And so we had to redefine our thinking and be prepared for it. So if I can just carry through with the story then, we came out of this hackathon going, oh my goodness, we're really glad we did this. We have a couple of months before this API goes live. How are we gonna solve it? We said, well, we obviously need to fix this one bug. And that was a really easy bug to fix. But the next thing that we said is, you know what, fundamentally, our biggest worry is we never want to allow anyone to fetch data they are not entitled to look at. And by doing that, we said, all right, we're going to define all of the data that our API can serve up. And we defined a standard interface in the.NET language, a standard interface that all of our data had to derive from. And we specified that there would be a middleware filter, and that middleware filter would check the data to see is the currently logged in user permitted to see this data object. And that solution meant that there was always an extra layer of security behind our API, which would protect this data from unauthorized access. It meant that it didn't matter how many bugs there were, we would catch them. And obviously we put a lot of effort into this. Do you know how many bugs we had after that?
Shawn_Clabough:
Probably
Mark_Miller:
Five.
Shawn_Clabough:
still had lots of bugs.
Ted_Spence:
Five? Five? I hear five.
Mark_Miller:
Yeah, five. I'm gonna say five. I'm going
Ted_Spence:
5...
Mark_Miller:
five.
Shawn_Clabough:
42.
Ted_Spence:
42, all right. Well, I will tell you that in the years that I managed the Avalara API, there was only once ever after where we got an error. And the thing is that error was caught by this extra layer of security. We had a system which would alert us the instant this happened, and it never happened because we were extremely careful during development. And only once when it went to the sandbox level, did somebody actually manage to trigger a security issue? And obviously then it was easy to fix.
Mark_Miller:
And was that that triggering of the issue unintentional? Was it accidental or was there some nefariousness or was somebody trying to, maybe on an internal red team, trying to trigger it?
Ted_Spence:
No, it was entirely accidental. It was, there was one of our technical sales engineers that was preparing a demo for someone and they had come across an API that they could trigger a security exception in. They're like, why does this thing trigger in a security exception? Never heard of that before. And that was the one and only ever time that this security layer was triggered in practice.
Shawn_Clabough:
Did
Mark_Miller:
So
Shawn_Clabough:
you
Mark_Miller:
this
Shawn_Clabough:
ever
Mark_Miller:
is.
Shawn_Clabough:
try to do any penetration testing on it? You
Ted_Spence:
Oh
Shawn_Clabough:
know.
Ted_Spence:
yes. So obviously the hackathon was the first stage in the process of building a robust API, because clearly what you do is you segment user data. So we store data for one user completely separately from data from another user, and you need to make sure that each user is completely and rigidly isolated from it. But then you find that there are a ton of APIs and people are always building new APIs and you have to test them all. And simply enforcing the security layer was really helpful because since we used an interface, we had another function that said, no API in our system can return data to a user that does not implement this interface. And so that was actually the thing that stopped our developers from adding bad APIs. Because if somebody's writing code and they start testing it, they're gonna get this error saying, you attempted to return data that doesn't implement our security interface rules.
Mark_Miller:
I love it.
Ted_Spence:
And this was the huge thing. This was the thing that really made the difference for us.
Mark_Miller:
Yeah, this is so cool. This is a little bit like the step from never unit testing or not having unit testing to going in and realizing, oh, I can use code to check my code.
Ted_Spence:
Exactly.
Mark_Miller:
I can use things like attributes or interfaces to specify how one piece of code communicates and connects to the other, and even in a meta level. right, at a meta level or stepping outside of that, in this case, looking at security, right? I think this
Ted_Spence:
And
Mark_Miller:
is such
Ted_Spence:
it's interesting
Mark_Miller:
a beautiful.
Ted_Spence:
that you mentioned the meta level too, because this is also the fundamental technique that meta or Facebook uses to manage its data as well. So you're probably familiar with over the years, Facebook has had to go in front of a lot of governments and answer questions about security. You've seen that on the news, right? Well, what they did internally is they went and defined contracts for all of their data objects. And they defined it using their own programming language. But the end result was basically the same. Every data object had on it an interface that controlled its rules. And those rules were examined before the data was used.
Mark_Miller:
Yeah, I love it. I especially love this idea that you cannot implement a new API without implementing that interface that's required, the security interface. If you don't, you're just rejected automatically because of that. And I really like that as kind of a low level defensive kind of strategy as you're building. And
Ted_Spence:
Indeed, and that's really the critical aspect of API design is what we're trying to do is to set forth patterns and enforce behavior from our developers because we're always under pressure. We're always about to ship something. We're always ready to launch new code, but we need to define what are the rules, what are the commitments that we make. And so this is just like how we have a test that ensures that our API is documented. We have a test on Swashbuckle. Are you familiar with Swashbuckle?
Mark_Miller:
I think I barely know it. I think
Shawn_Clabough:
Yeah.
Mark_Miller:
I've heard of it. I
Ted_Spence:
It's the open API implementation for sometimes called swagger, at least it used to be known as swagger before they changed the name to open API.
Mark_Miller:
Right, I've used it a little bit, that's it for me.
Ted_Spence:
Well, in Swagger, you can define documentation for your API. Well, if you don't do that, your API is going to be really hard to use. So we have unit tests that examine all of our objects and they throw an assertion error if there's no documentation for a method or if there's no documentation for an object or if there's no documentation for a field. So if somebody tries to add an API in a rush, they're gonna see a unit test fail and we stop them from shipping something without thinking it through.
Mark_Miller:
And is that across the board regardless of visibility or is it only for public facing API calls and classes?
Ted_Spence:
Well, this is a constant debate and I've faced this problem a number of times. I come down on the side that there should be no hidden non-public APIs. And the reason I say this is not because I think every customer out there should be able to use every API. It's because when I hire an auditor or when I hire a penetration tester, I want to be able to show them these are all of my APIs, including the ones that are sensitive.
Mark_Miller:
Right.
Ted_Spence:
I don't want them to have to guess and luck into something. I also don't want something to be hidden and have an end user or a hacker out there discover it.
Mark_Miller:
So do you have a sense that there's a frictional force against speed of development, since now everything needs to be carefully documented, right? We're moving forward maybe more carefully, unless. directly less slicing through the problem. We are carefully approaching the problem. I guess there's maybe, I guess a two-part question. Do you have a sense of a quality increase and do you have also a sense that maybe there's a slowdown? Is there an impact on speed of development and is there an impact on quality?
Ted_Spence:
Well, there's an impact from everything that you do as a team. And I think it's important for your team to agree on what the right level of checking to have. Because any time you as a team make a commitment, you need to have universal buy-in. If you don't have universal buy-in across your teams, they're going to circumvent whatever measures you have. Another example would be if you're working in TypeScript. It's possible to develop TypeScript while still allowing people to use the keyword any. But most TypeScript developers that I know of tend to forbid any as a type. And I think that's just a decision. Like you can make that as a team, but you have to ask your team to buy in. So I've recently started working with Project Manager. It's an excellent company based, most of the engineers are based out of New Zealand. And they have very different decisions. than I have historically, but as a team, they come together and they make decisions on what they are willing to do as a team, what commitments they're looking to make. And we document those decisions and we implement them and we have unit tests for them.
Mark_Miller:
Souds, souds.
Shawn_Clabough:
I could see people circumventing that by just going, my documentation is to do, document this.
Ted_Spence:
It's quite common.
Mark_Miller:
So I get total buy-in, but I'm still looking for a sense,
Ted_Spence:
Mm-hmm.
Mark_Miller:
and answer that question. Do you have an overall, looking back
Ted_Spence:
Mmm.
Mark_Miller:
on this time where you've had universal buy-in, where you've gone in and said, OK, we're going to go in and we're going to add this extra layer of security. We're going to add these checks that are essentially going to throw errors or fail test cases if we don't have documentation. Do you have a sense? of the impact of that. When you look
Ted_Spence:
Well,
Mark_Miller:
back on that.
Ted_Spence:
what I will say is that there's a different factor that I also see, which is people afraid to commit code. I will often find that a developer is working on a sensitive API and they say, you know, I just need another day to look at this. I'm really worried about this API. I need another day to look at it. And so in a lot of cases, developers will be cautious and they'll slow roll individual pull requests. And what I see is that these tests, they may slow down the actual writing of code, but they speed up. by eliminating some of the worries and the hesitations that developers have. I don't know, I do think that it probably slows people down, just like I'm sure there's developers out there who would rather write in Ruby, which is a kind of open typing language rather than a language where types are strictly enforced. There's a good comment, have you heard people talk about Rust, where they talk about the type safety and enforcement in Rust?
Mark_Miller:
I haven't.
Ted_Spence:
Well,
Shawn_Clabough:
Yeah, I haven't looked
Ted_Spence:
this was
Shawn_Clabough:
at Rust.
Ted_Spence:
going
Shawn_Clabough:
I've
Ted_Spence:
around
Shawn_Clabough:
heard
Ted_Spence:
a
Shawn_Clabough:
a lot
Ted_Spence:
lot
Shawn_Clabough:
about
Ted_Spence:
when I
Shawn_Clabough:
it,
Ted_Spence:
was
Shawn_Clabough:
but.
Ted_Spence:
working at Facebook, and people would say, you know what? Rust requires me to define everything. It requires me to label everything. It requires me to give every object a life span. But at the same time, they'll say, I can just write whatever code I want and I'll trust that it will work. So I kind of like that philosophy. It's a good approach. It's use tools to help guard yourself against obvious mistakes.
Shawn_Clabough:
So it seems a lot of this is kind of at the authorization level of security. How does this apply to the authentication section of security?
Ted_Spence:
Oh, that's a good point. Obviously, some companies treat authorization and authentication as a single element, and some companies treat them differently. What I find is that I have a lot of security rules that say this object is restricted to people who are an owner of this account. So that's a very common security rule. Like, this object is sensitive information about the account, the only people who can view it are owners. just having authenticated, it isn't sufficient to reach that level of security rule. But on the other hand, the authorization level where you check the user and you check their permissions, that's where you get a lot of value out of this. But on the other hand, there are some simple APIs, such as just fetching some data or retrieving something that really only require validating that a user is entitled to log in. I wonder if I could think of a good example of this. At Avalara, we had informational APIs. And those informational APIs were just about free data about governments that we were willing to provide to anyone. And so those APIs would require authentication only, but no authorization rules. I hope that helps. That may have been a bit...
Shawn_Clabough:
Yeah, yeah, definitely.
Ted_Spence:
Everybody
Shawn_Clabough:
Definitely.
Ted_Spence:
loves talking
Shawn_Clabough:
Yeah.
Ted_Spence:
taxes.
Shawn_Clabough:
Well, I just did my so.
Mark_Miller:
Nice.
Shawn_Clabough:
Got that done out of the way.
Ted_Spence:
How much
Shawn_Clabough:
So.
Mark_Miller:
Good.
Ted_Spence:
did you owe? Oh, I can't ask you that.
Mark_Miller:
So Ted, if I can share my experience with you, we made, like I want to say decade plus ago, we made a transition from having a few test cases to being very disciplined about writing test cases. And a few things that I noticed in that process. One of the things that I noticed is that the developers all noticeably more proficient and faster at writing test cases than they were when they first started. So that's observation one. Observation two is we started getting a blanket of what I call a nice blanket of test cases, something like 20 to 40,000 I think around that level. I started feeling incredibly confident every time it was time for a new release. Right, you'd run those test cases, everything's green, you have a really strong sense that you're not breaking anything, right, when you put that out there. And part of this was fueled by an internal policy that said, if there's a bug, we write a test case first. We cannot clip fix or close a bug without a test case. And then secondarily, an active search for ways. to throw more data at the test, to process more data through the test cases. So we were able to essentially automate the creation of test cases in many ways. And so overall, initially, there's a slowdown, right? So I guess you notice an initial kind of slowdown in progress, and there's more effort and focus and concentration. because you can't just write the feature and dismiss it, right? You have to give it more attention. But what happens is that your quality does go up, at least for me with the test cases, where, like, yeah, quality is definitely going up. The quality of this code is higher. And the second piece that was a bit of a surprise was just the proficiency of the developers. They're getting a little bit better, right? They're getting
Ted_Spence:
That's
Mark_Miller:
faster
Ted_Spence:
a really good
Mark_Miller:
at
Ted_Spence:
point.
Mark_Miller:
that.
Ted_Spence:
If you force people to use their own APIs, if you force people to write tests for their own APIs, they need to think through how it works.
Mark_Miller:
Yeah, it's really good for an API, right? To design the API from the client's perspective, because that's what you're doing, right, if you have to write test cases for it. Yeah.
Ted_Spence:
And if I can put a pitch in there, I'm also a big fan of integration tests. And integration tests get a lot of a bad name from people, because integration tests are often considered slower than unit tests. They're often considered more brittle. But the idea of an integration test is that it actually works against a fully running system.
Mark_Miller:
Right.
Ted_Spence:
And I wrote a very large suite of integration tests at Avalara. And those integration tests caught all sorts of interdependencies that unit tests couldn't see. Because they would encounter cases where two differing systems within our company would talk to each other in ways that seemed perfectly fine in practice, seemed perfectly fine in theory, but fell apart in practice.
Mark_Miller:
And then when you catch that, what happens then? What
Ted_Spence:
Well,
Mark_Miller:
do you do?
Ted_Spence:
it's
Mark_Miller:
Do
Ted_Spence:
really
Mark_Miller:
you just,
Ted_Spence:
tough.
Mark_Miller:
do you
Ted_Spence:
You
Mark_Miller:
just?
Ted_Spence:
have to reach out to other people and convince them that your test shows something.
Mark_Miller:
Right,
Ted_Spence:
And it's not easy.
Mark_Miller:
and do you keep just the integration test? Does the integration test now become the only flag, or do you go back to the code and say, wait, our unit tests were not covering certain combinations. We need to go back in and fix that, address that, which could then lead to an architecture change, right? So that you can go in and actually connect these two pieces together and see it. Like, does that happen? Did that happen with you? Any kind
Ted_Spence:
It
Mark_Miller:
of.
Ted_Spence:
does happen, but I also find that just temperamentally, a lot of my developers were 100% on the side of unit tests or 100% on the side of integration tests. So
Mark_Miller:
Oh really?
Ted_Spence:
oftentimes, some people would just be completely like, oh, I won't ever touch this integration test thing. And it would take time for them to be convinced that it was a real issue.
Mark_Miller:
Yeah, I am like, I'm that guy. I'll tell
Ted_Spence:
Hahaha!
Mark_Miller:
you, I'm the guy who's a little nervous of integration tests because I consider them incredibly complex and when I watch them run, so I don't write them, I don't think I've written a single integration test, but when I watch them run, it's a little scary because all of these windows are popping up, this is an application running on Windows, so they're just popping up really fast. I'm looking up and going, how are we testing anything? How can this be real? How can this be safe? This is what's going on. in my head, right? I'm that guy who I find incredible safety in unit tests, especially really well designed architecture that can really allow it to be fully deeply tested. I am that guy. That's where I, that's the island I want to stand on. The integration island is scary to me. The
Ted_Spence:
It
Mark_Miller:
integration
Ted_Spence:
is definitely
Mark_Miller:
test.
Ted_Spence:
scary. And like an example of the type of thing that it helped us to find was we would have this incredibly low level of a problem that didn't make any sense. The problem was, you know, failed to create a transaction for this company and be like, what's going on? Why is this transaction failing? Everything about this was exactly correct. And eventually we got an integration test that discovered that if you created a company and started using it within the next 200 milliseconds, things would fail.
Mark_Miller:
Uh, yeah.
Ted_Spence:
And well, okay, it didn't show up in the unit tests because the unit
Mark_Miller:
Right.
Ted_Spence:
tests asserted that everything was either not done or not done.
Mark_Miller:
It's beautiful. So how do you, so what do you do? How do you get that? Like, you know, for me, turn, you know, the first thing I wanna do is I wanna say, okay, integration tests discovered it, but now we've gotta go back, we gotta fix the architecture, we gotta create a unit test. Did you guys end up doing that? Is that what happened in this case? Or, or, or, or, or, or, or, or, or, or,
Ted_Spence:
You
Mark_Miller:
or, or,
Ted_Spence:
know, I
Mark_Miller:
or,
Ted_Spence:
wish
Mark_Miller:
or,
Ted_Spence:
I could say exactly what we did about this one. I have to confess this issue is probably seven years old now, and I don't remember exactly the answer. But I think the solution was perhaps just as simple as addressing the delay in generation of the object.
Mark_Miller:
Yeah.
Ted_Spence:
So like one API call would generate the object. Well, that API call needed to wait until the object was fully generated before it could return.
Mark_Miller:
Yeah. So this is super cool. Documentation. So let me tell you about my personal experience with documenting existing functions and API calls. So I create an API that's essentially only used by my team. That's it. Or you could even argue by me, because a lot of times when I'm doing my live streaming, I'm creating stuff that's for no one's consumption, but my own. And... When and on the live stream, when I'm coding live, I will rarely write out documentation because it's slow to do and it's kind of boring to watch. However, I will sometimes do it. And in the times that I've done it, in the methods that I have created documentation for, boy, do I feel relieved about a year later when I'm going in and looking at it, right? Wait, and I have a question. Right? Because in the moment, I might do something a little quirky, because I'm under pressure writing code live, doing it fast, that sort of thing. And that quirky thing I did, I've forgotten all about after a year or a couple months, whatever, and I go back in. So I do acknowledge, it's like I acknowledge that it's like taking care of myself is a good thing to do. I acknowledge that, right? Riding my bike slowly around corners would cause me to fall less, right? Then if I go, you know, high speed, I acknowledge the right thing to do. And sometimes I do it. When I do it, I feel good. But yeah, there is a... For me, there is kind of... There's a slowdown, I notice, right? It's a change. It's a change in your attitude. You know, it's like
Ted_Spence:
Totally.
Mark_Miller:
this buy-in idea that you're talking about, right?
Ted_Spence:
Well, there's other ways to think of it. Sure, you're slowing yourself down because you're writing extra stuff and you're making yourself think through how you would define it in English language or whatever your preferred language is in comments or in documentation. Sure, you're defining that, but there's that great anecdote. You write the code once, but you read it eight times. So you're gonna come back eight times over the course of this project and you're gonna come back and read the code. Maybe it's gonna be soon and you'll remember everything. Maybe it's going to be, as you said, a year later, and you won't. So you got to prepare yourself for the fact that sure, you're slowing yourself down in the moment, but the end result is that you'll get more velocity later on. Another way I like to tell people is that writing comments and documentation is your way of rubber ducking your own code. So if you don't have a pair programmer to work with you, some people will say they'll get this little rubber duck. And I have a little ceramic duck that I keep on my desk here. So I talk to them every once in a while. But some people will keep a duck and they'll talk through the code to the duck and they'll explain it to the duck and that will help them think through and spot edge cases. Well, you can use documentation. You can use comments and documentation to be just like this duck. By writing it down, you're gonna help yourself spot those problems that you might have overlooked if you were just rushing out the code.
Mark_Miller:
Yeah.
Shawn_Clabough:
and
Mark_Miller:
Yeah,
Shawn_Clabough:
one
Mark_Miller:
this
Shawn_Clabough:
quack
Mark_Miller:
is.
Shawn_Clabough:
it's good and two quacks it's bad?
Mark_Miller:
Hahaha!
Ted_Spence:
Well, I will tell you, there's a lovely creek nearby my house called Longfellow Creek, and a couple months ago I saw this absolutely beautiful duck called a spotted mergenser, and it was the most beautiful duck I've ever seen in my life. If you have a chance to look it up, spotted mergenser online. Absolutely gorgeous, was way too shy. I couldn't coax it out for a good photograph, but you gotta love nature here in the Pacific Northwest.
Mark_Miller:
Yeah, I think you just totally doxed yourself, Ted. We...
Ted_Spence:
Well, I do live on the island of West Seattle, and we are finally reconnected to the mainland now that our bridge has been working for the past year or so.
Mark_Miller:
That's like everything I need to know. I know exactly where you live now. I'm
Shawn_Clabough:
Hehehe
Mark_Miller:
zooming
Ted_Spence:
Oh, totally.
Mark_Miller:
in on your home.
Ted_Spence:
Go right ahead. Well, you know, I publish these things and I do blog on TedSpence.com. So I have lots of articles up there about API design. And if I can share one other interesting little tidbit, are you familiar with GUID record IDs, like record IDs being generated as GUIDs?
Mark_Miller:
I guess kind of.
Ted_Spence:
I guess kind of. So a lot
Shawn_Clabough:
Thanks
Ted_Spence:
of
Shawn_Clabough:
for
Ted_Spence:
websites
Shawn_Clabough:
watching!
Ted_Spence:
nowadays, a lot of APIs are generating records as GUIDs because they're pretty convenient and they're pretty unique and they're easy to generate. Well, there is a real annoying usability problem with GUIDs, which is that if you want to copy and paste a GUID, you try to double click on it, you can't copy and paste the entire GUID. If you double click on it, it will only select part of the text.
Mark_Miller:
Right.
Ted_Spence:
So I have an article up on my website where I use a technique from Bitcoin. to generate what's called a base 58 string. And the base 58 string is an example of a single long string that you can just double click and copy and paste, but it converts under the hood into a GUID. So you can just basically write a little.NET mapping layer to convert these strings into GUIDs and back again. And it just makes your API a little bit easier for devs to work with.
Mark_Miller:
That's really interesting. You know, for one of the projects that I was working on, we had a bunch of log files loaded with GUIDs. And one of the things I did, we didn't have the problem with copying and pasting, but we had the problem of human analysis, right? Going in and looking at it. And what we ended up doing is creating an alias system. So what we did is I basically grabbed a dictionary of five and six letter words, words that were about that long. I... I cleaned up, took out words that were really close together, that were only off by one letter. And then we just started doing aliases. So we could very quickly look at aliases and say, oh, this is, you know, hashtag banana. And oh, there's another banana. I can very quickly spot those right there and see those. Right?
Ted_Spence:
Brilliant. I love it.
Mark_Miller:
Kind of interesting. Yeah. Yeah, well,
Ted_Spence:
Yeah,
Mark_Miller:
you know,
Ted_Spence:
you know,
Mark_Miller:
go ahead.
Ted_Spence:
I was just going to say logging is an absolutely fantastic thing, but it's so hard when you get a volume of logs that are just mind boggling to deal with. And I love that technique. It's like giving yourself something simple and easy that you can search through very rapidly, giving yourself a tag that you can identify. That's great.
Mark_Miller:
Yeah, no, I found it really super useful for going in because you'd be looking at a page in a spreadsheet and there would be, you know, imagine, you know, 20 GUIDs up there and now replace it with like five words and they're showing
Ted_Spence:
Mm-hmm.
Mark_Miller:
up there, it's so much easier to say, okay, there it is, there it is, there it is, and trace
Ted_Spence:
I'm so
Mark_Miller:
it and
Ted_Spence:
glad
Mark_Miller:
follow it
Ted_Spence:
we've
Mark_Miller:
in.
Ted_Spence:
gone past the era of regular expressions just searching through raw text. I mean, maybe maybe you dealt with this 15 years ago, like I did, but I used to use log systems where it would just be one massive stream of bytes. And all you would do is just write a regex to search for what you wanted. And hopefully you'd find something and chances are you never would. Nowadays,
Mark_Miller:
Wow.
Ted_Spence:
we have structured logs and really good systems and I love it.
Mark_Miller:
Yeah.
Shawn_Clabough:
So is security handled any differently, whether you're a small group or a large group?
Ted_Spence:
Well, it's really just a matter of when you're going to pay the price. I'd say sooner or later, you're going to get hit with it. I've worked for small companies that tried to do something extremely rapidly and extremely quickly, and it just meant that, you know, they would push that day off a year or two to when they would have to face the security challenges. You know, back in the 90s, we used to be able to protect ourselves just with firewall rules, but that's not really possible anymore because everything can go through your API, everything goes through HTTP. So for a small company, sure, maybe you wanna get started without doing everything as exhaustive as possible, but there are some great templates. I have up on my GitHub account, an example API that I'm building to try to show off these techniques. It doesn't have to be slow. You could design an API that uses these techniques and you could still make things happen really quickly. But, you know, maybe that's just maybe I've been doing it for long enough that I'm familiar with it.
Shawn_Clabough:
One of the one of the other issues I've always had with documentation is quite often the documentation doesn't Keep up with what the code is actually doing You know you write the the comments or the code the documentation when you first write the code Then you have to go back at some point in time and you make changes to the code and you don't update the documentation So quite often it's stale compared to what the code is actually doing
Ted_Spence:
That's a common complaint. When I was working at Facebook, people would send reviews on my pull requests, which were called diffs at Facebook. They would send reviews and say, remove this comment. And I'd say, why? And they'd say, well, it's just going to get stale and then it's gonna be a problem. I'm like, well, it's accurate now. And they'd say, well, it's going to be inaccurate at some point in the future. I personally like that. I find it a kind of redundancy. It's something like a CRC for your code. If you write the same thing twice
Mark_Miller:
Thanks for watching!
Ted_Spence:
and you make a mistake or somebody changes it, you can see, hey, wait a minute, this was changed from its original definition. I need to rethink this and say, is this comment correct or is the code correct? I find that personally useful. I know a lot of people don't, and I don't begrudge other people their disagreements with me because we live in a world of a million people with a million different ideas.
Mark_Miller:
Oh, I'm going to begrudge them Ted. Because
Ted_Spence:
There we go.
Mark_Miller:
I'm
Shawn_Clabough:
Yeah, I
Mark_Miller:
on
Shawn_Clabough:
mean,
Mark_Miller:
your
Shawn_Clabough:
buddy,
Mark_Miller:
side. I'm on
Shawn_Clabough:
yeah.
Mark_Miller:
your side on this one. Yeah, I actually I actually there have been times where I've noticed oh look at this XML doc comment in my C sharp code. It's highlighted differently. Why is that? Oh, because I manually deleted a parameter but didn't take care of the documentation. In other words, the syntax highlighting can sometimes reveal an out of sync state with the documentation. Also kids, we're moving into AI pair programming world, and we're not far from a point where the AI, I think, is going to be able to highlight methods, where the code description, the natural language description of what it's doing, can be flagged as it doesn't look like this method is actually doing that. is way better than not ever having a comment there ever at all, right? Because the method name, for example, might reveal something or something, you know, what you think is happening, but it's really maybe not happening, right?
Ted_Spence:
Totally, and I teach my students at Bellevue College, I teach them that it's really critical to just put citations in things. So when I teach them how to write a Jupyter notebook, for example, I say, look, you're going to be using techniques from each other. You're all going to be collaborating together to create data analytics solutions. I want you to put a comment in and say, so-and-so taught me how to do this. I want people to learn this as they go into the industry because this will stand them in good stead. I love it when people give credit to each other. for the contributions that we make. I write a small amount of open source code. It's not super popular, but there's a couple of people who use my stuff and I am always grateful when I see somebody say, wow, I use Ted Spence's solutions here.
Shawn_Clabough:
So what are your thoughts on the approach of just doing self-documenting code?
Ted_Spence:
Well, self-documenting code oftentimes is not. So people may look at something and say, it's perfectly self-documenting for me. And then I come back and look at it a month later and I say, wow, I'm not sure what they were using. So, an example of this is in a big enterprise code base, you may have a business user, you may have a business user administration page, you may have a business user administration page settings toolkit. You may have a business user administration page settings toolkit mutator system. Well, OK, like sure, you've got a system that's got a whole bunch of names. But can you actually take all of those names and interpret how it's different from some other thing? You know, self-documenting code only works for the person who wrote it. Somebody else may have a good time figuring it out. They may have a bad time and you don't know and you can't be sure.
Mark_Miller:
I think a rule that I would agree upon if I was on your team, Ted, would be that we could claim it as self-documenting if someone else can look at it and agree. You can't claim your own code that way, but somebody else could claim it. That would be maybe a threshold that I would agree to.
Ted_Spence:
Well, there's a great phrase from Einstein, which is that everything should be as simple as possible, but no simpler. Sooner or later, there's complexity. Writing code is complicated. Writing good code is even more complicated. And I think of code not so much as engineering, but more like the law. Somebody out there is writing a law and they're trying to cover all of the edge cases that are going to be hit in the real world. And they may do a good job or they may not do a good job, but that law is going to exist either way. We are writing code, we're trying to handle cases. We're not assembling girders and bricks and cement. We're taking rules to handle data and behavior and interactions. And it's tricky to get it right.
Mark_Miller:
Amen.
Ted_Spence:
There we go. I should also ask, are you familiar with the card game Magic the Gathering? Have you seen that here in the various cafes in the Pacific Northwest?
Shawn_Clabough:
I've seen it. I've never played it.
Ted_Spence:
Ah,
Shawn_Clabough:
I think my son
Ted_Spence:
well,
Shawn_Clabough:
probably has.
Ted_Spence:
I like to tell people that laws are like magic the gathering. Every individual rule makes sense when you look at it on one card, but when it comes into conflict with other cards, you don't really know exactly what's going to happen. There's no way to guarantee exactly how people are going to interpret them. And code is difficult. When you have multiple systems interacting together, you're not sure whether stuff that is a unit-tested, perfect, is going to work exactly the way that you want when it's put into the real world against another one. And that's why I like integration tests.
Shawn_Clabough:
Okay. So, and we've talked quite a bit about, you know, security and API documentation and things like that. Is there anything we haven't covered that we should go over?
Ted_Spence:
Well, I'd like to say no matter how much work you put into your API, you're going to find somebody out there that disagrees with you. And you have to be prepared that you're never going to get everything you want, and you're never going to have everyone sign off on your decisions. I have a lot of good articles up on my site about design decisions I've made. Things like, do I use enums? Things like, what should my data layer look like? Things like how should I fetch multiple objects? Should I use GraphQL? Should I use OData? All sorts of choices out there. Nobody is ever going to be perfectly happy with what you've done, but if they can figure out how to solve their problem, they'll probably be okay with it. So that's why I like documentation so much.
Shawn_Clabough:
Yeah, I have to agree with you there. Okay.
Ted_Spence:
My little favorite story is at one point a very large company, very big firm called me up and said, Ted, we're going to start using the this new API that you built for Avalara. We're going to start writing code on it. We want a meeting with you on Monday. So line up all of your best engineers and explain to us how to use the API. So I lined up all of my engineers. I prepped everyone. Well, the meeting was canceled on Monday morning. I said, Oh no, what happened? Did you change your mind? They're like, no, one of our developers found your documentation over the weekend. It's already done. We don't need the meeting anymore. So I liked that story. I thought that was a good one.
Shawn_Clabough:
That's awesome. Yeah. Cool. All right, so I guess time to move on to picks. Mark, do you wanna go first with your pick? The
Mark_Miller:
Yeah, I'm going to do it. This is, you know, last week I brought everybody to London to go see Hamilton. This week, I'm
Shawn_Clabough:
I'm
Mark_Miller:
going
Shawn_Clabough:
sorry.
Mark_Miller:
to do something slightly different. We're going to London again. But this time it's going to be Sherlock Holmes, the escape room or the the great game. I'll get you a link in a second. I don't remember exactly what it is, but this this is one of the things that we we ended the kids Easter egg hunt with and it's one of the most beautifully designed escape rooms that I've been in. Now, I'm an expert on good design, and there were a few places where they failed good design, almost on purpose. They obscured some clues, made them hard to see. They'll get my notes. Don't worry. I'm going to send those to them. But on the whole, it felt incredibly immersive, a wide variety of puzzles, different kinds of things that you had to solve. It was always changing a sense that you were actually in it with Moriarty and Sherlock Holmes from the BBC series Which is excellent by the way if you haven't seen that I strongly recommend going you know hitting that first But if you're in London, and you like escape rooms this thing was amazing And just a side story on this on the outside to get in you have to go up to a to a store in a mall that's the name of the story is Doyle's Opticians. You have to ring the bell and say you're there for a routine examination. And that's when they bring you in through the optical store and then take you into the back, give you an eye test and then reveal that it's actually the way to bring you in to the secret service, the British secret service. And you're gonna do some tests and Moriarty comes back from the dead to interfere and. And at some point, you're you're, you know, Sherlock Holmes is saying, do what Moriarty says for now, I'll try to do my best to help out. And you're getting pulled in. And you realize that you are you're you solving the puzzles is actually going to lead to Moriarty's plan being fulfilled. And you realize that you're about to release poison under the citizens citizens of London. And maybe I've said too much, but it was amazing. And that part was really well done near the end. I loved it.
Shawn_Clabough:
I've not had a chance to do an escape room, but it does sound like a lot of fun. Do most of them have like, you know, I want an easy room, I want a medium hard, or I want a difficult room?
Mark_Miller:
I don't think so. Most of the places just give you a variety of themes to go after. One of the common themes is like a Pharaoh's tomb or something like that. You're trapped, you've got an hour to get out before the curse of the Pharaoh traps all of you and permanently You know, I think that, you know, a good escape room, I think gives you a sense of being there. It gives you puzzles that that maybe take a moment to figure out and then while you have to figure it out, there's still logic involved in in other words, you first have to figure out how to solve the puzzle, what the approach is. And then second, once you know how to solve it, you still got pieces you've got to put together, you know, that sort of thing. Like if I give you a jigsaw puzzle, at some point, it'll take you a second to realize, oh, I got to put these pieces together if you've never seen one before. Right? So there's general two stages to solving a particular problem and I love it when escape rooms build on accomplishments. So you've done one thing and now you can use that to solve maybe another problem, that sort of thing. But typically
Ted_Spence:
If I
Mark_Miller:
they...
Ted_Spence:
can throw my pitch in here, there's a place called Escape Artist here in West Seattle, and they have an escape room that I think is really phenomenal. It's called Dive to Atlantis. And why I think it's phenomenal is that it's a story, that rather than just being a bunch of puzzles you have to solve, you are put into a situation where you're told, okay, there's a treasure, and you have to follow all of these clues to get to the treasure. And at no point during the escape room... Did I feel like I was just solving random logic puzzles? It felt like I was trying to get into the submarine, trying to figure out how to make the submarine work, trying to get to the bottom of the ocean, then trying to break into this temple. And
Mark_Miller:
Yeah.
Ted_Spence:
it was just fantastic. So
Mark_Miller:
No,
Ted_Spence:
I
Mark_Miller:
I
Ted_Spence:
can
Mark_Miller:
love,
Ted_Spence:
wholeheartedly
Mark_Miller:
yeah.
Ted_Spence:
recommend it.
Mark_Miller:
Yeah, really well done, right? So the idea, yeah, good escape rooms have great puzzles. They have a sense of story, a sense of progression. They have rooms that lead from one to another. And now you get into a new room, and you've got to figure out what's going on in this room. And yeah, I think that in general, there was like a brilliant moment. Yeah, there were several brilliant moments in the Sherlock Holmes escape room. In the one in. inside of in London, I highly recommend it. I'll get you a link here in a second, Sean.
Shawn_Clabough:
So if you get stuck, is there a way to get hints or you just have to wait until
Mark_Miller:
Yes.
Shawn_Clabough:
your time's out?
Mark_Miller:
Yeah, most escape rooms give you an ability to ask for a hint of some kind. Like you can, I've been in escape rooms where they have a yes or no answer. Right? Well, policy. So you can ask a yes or no question. Like, am I supposed to do this? Do I correctly understand how to solve this puzzle? And they can say yes or no on that. You know, that sort of thing. So yeah, you can get hints. The goal isn't for them to beat you. The goal is for you to have fun with your group or party that's in there. And so a really good escape room will also have the ability to nudge, right? To nudge you a little bit. That's in the right direction. So yeah.
Shawn_Clabough:
Yeah, I remember
Ted_Spence:
I've
Shawn_Clabough:
watching
Ted_Spence:
had cases
Shawn_Clabough:
some.
Ted_Spence:
where they called me on the headset or on the walkie talkie and they said, oh, I'm sorry, that light that you're looking at is broken right now. That's why you can't see that puzzle solution. So like just helps you when machines are having trouble.
Shawn_Clabough:
Yeah, I remember watching some reality show competition thing that had a lot of puzzles and things like that. And I remember they got to the end and they were in this kind of like a tomb type thing and they were given a trip decks and just had to sit there and wait until somebody finally figured out the the the code for the trip decks, whatever. It ended up being FS key because a lot of the story was based upon Francis Scott and things like that. So finally, you know, I don't know how many hours they were there. Of course, it wasn't that long on the show. But finally, somebody just figured out, okay, FS key, they got out and they won $3 million between the three of them. So
Ted_Spence:
Amazing.
Shawn_Clabough:
yeah, I like those steps. Thanks.
Mark_Miller:
I didn't get any money from these SysCape
Ted_Spence:
Hahaha
Mark_Miller:
groups. They just took my money.
Shawn_Clabough:
Yeah, so my pick this week, have you heard of bionic reading?
Mark_Miller:
I have not.
Shawn_Clabough:
No, no,
Ted_Spence:
New to me.
Shawn_Clabough:
it's. So if you've got ADHD or anything like that, a lot of developers do. There's this thing called bionic reading. And basically what it does is it changes the font style of certain parts of words to make it much easier to read. So it will bold, you know, certain parts of the word. And so your eyes will kind of, you know, fill in your brain, kind of fills in the spaces just based upon the bright letters and things like that. So it's it's at Bionic reading dot com. And.
Mark_Miller:
Okay, I'm freaking out right now, Sean. This is awesome because
Shawn_Clabough:
Yeah.
Mark_Miller:
I'm like, I'm immediately guessing. I'm guessing that the parts they're highlighting are the beginning and last letters or something close like that. Are they?
Shawn_Clabough:
No,
Mark_Miller:
Is that what? What?
Shawn_Clabough:
so no, it's quite different. So I mean, I guess it is quite often the beginning looking at their example here, but it will change. And I guess for the most part, it's probably the first letters looking at the example. So there's apps for your phone, you can do that. There's extensions for Chrome. I guess the default Chrome extension for it is not very good, but there's one called Jiffy Reader that works better for Chrome. And then Mads Christiansen actually wrote an extension for Visual Studio that will do it in your code as well. So. If you're interested in just kind of checking it out, look at bionic-reading.com and then I'll put links to the Visual Studio extension in the show notes, but you can also just, you know, search on bionic reading Visual Studio extension and it'll bring that up for you.
Mark_Miller:
This
Shawn_Clabough:
So.
Mark_Miller:
is very cool. It looks like it's really highlighting the first part for the most part of each word. And it must be doing it in a way that it's trying to find uniqueness in this.
Shawn_Clabough:
Yeah, yeah, I don't know their actual algorithm for doing it. But you know, when I looked at just their samples. it made it so much easier and quicker for me to to read quickly through what I was looking at. You know, especially with, you know, a mild case of ADHD that I am typically not patient enough to sit there and read something that's very, very long. So the faster I can read it, the better it is for me.
Mark_Miller:
I love it.
Shawn_Clabough:
So, yeah. All right, Ted, you got to pick for us this week.
Ted_Spence:
Well, I'll give you one of my favorite things to do here in the Pacific Northwest. Coming up in June, there is a fantastic event called the Georgetown Carnival. The Georgetown Carnival is this amazing street party. There's all sorts of food, there's activities, and there is the world famous Power Tool races where they set up chainsaws and they race them down the street. Has to be seen to be believed. You'll love it. It's really just one of those weird quirky things that Seattle does incredibly well. I know this isn't technically Seattle, it's the Georgetown neighborhood, but same sort of thing. I love the weird parts of Seattle. I'm really glad it's coming back after the pandemic.
Mark_Miller:
Okay, these pictures are insane.
Ted_Spence:
Aren't they great?
Mark_Miller:
The track looks like it's made of wood, and is it, do the cars actually tear up the wood? It looks like there's sawdust all around the track.
Ted_Spence:
They sure do. I've been there a couple times and oh boy is it noisy and oh boy is it hilarious and it's just a ton of fun. They usually have a booth set up by Equinox Studios as well and Equinox Studios puts up a sign that says, Art Can Kill You and then they have amazing things like the Georgetown Virtual Roller Coaster. Gotta be seen to be believed.
Mark_Miller:
And are these other vehicles powered? Are they powered by a cord that goes to them? It looks like that might be the case. Like they actually are towing along a power cord or are they battery powered? Do you know?
Ted_Spence:
You know, it's been a while since I've seen them, but I remember them being courted at the time.
Mark_Miller:
Yeah, these look corded, totally corded
Ted_Spence:
Mm-hmm.
Mark_Miller:
to me. So we're gonna get spinning blades
Shawn_Clabough:
Hehehe
Mark_Miller:
and electrical cords, and we're gonna race on a track of wood. I'm
Ted_Spence:
What's
Mark_Miller:
in. I know.
Ted_Spence:
not to love?
Shawn_Clabough:
You first talked about power tools, and I was thinking that they were going to be using like a power drill to power their little soapbox, derby car or something like that, not chainsaws.
Ted_Spence:
Well, my daughter went there a couple of years ago and they helped her to weld a metal flower to a public art installation. Year before that, she used a blowtorch to create art. It was just so much fun and so much weird experience that you wouldn't get just sitting at home.
Shawn_Clabough:
Very cool. All right, Ted, if our listeners have questions and they'd like to get in touch with you, what's the best way to do that?
Ted_Spence:
Well, I spend most of my time now on Mastodon. You can look me up at tedspence at indyweb.social. You can also find me on LinkedIn. You know, I write there from time to time and would love to see you at Bellevue College if you ever come out on campus.
Shawn_Clabough:
Very cool. All right. Yeah, my sister-in-law used to live in Redmond there, Kirkland Redmond there. So and I still have family down in Puyallup. So if I make it over there, I'll get in touch. Great.
Ted_Spence:
Love to it.
Shawn_Clabough:
If our listeners have feedback, like get in touch with me or the show, they can get me on Twitter. I am at.NET Superhero. And where are you at, Mark? Just Twitch.
Mark_Miller:
Uh, yeah, I'm pretty much just Twitch. I kind of, you know, bumped off of Twitter. I set up a Maston account, but I didn't really do anything with it. So yeah, just come see me at Twitch. Twitch.tv
Ted_Spence:
There you go.
Mark_Miller:
slash code rushed. I'm live there for goodness sakes. Does
Shawn_Clabough:
Hehehehe
Mark_Miller:
it get more social than that?
Ted_Spence:
Well, look forward to you streaming some games there too.
Mark_Miller:
Oh, I don't do the games, Ted. I'm only
Ted_Spence:
Ah!
Mark_Miller:
writing code, man. I have no time for games. It'll
Ted_Spence:
All right.
Mark_Miller:
take. I won't come out. If I dive into a game, I won't come out. And there'll be no documentation for my API.
Ted_Spence:
Hehehe
Shawn_Clabough:
Not unless Ted gets there and tells you to write some documentation. Hehehehe.
Mark_Miller:
I
Ted_Spence:
Well,
Mark_Miller:
know.
Ted_Spence:
it's been a pleasure to be here, but I can't spin off documentation as fast as I'd like, so you'll have to invite me back sometime.
Shawn_Clabough:
All right. Yeah, it's great to have you on the show, Ted. Thank
Ted_Spence:
Thanks
Shawn_Clabough:
you very
Ted_Spence:
so
Shawn_Clabough:
much.
Ted_Spence:
much.
Mark_Miller:
Thanks,
Shawn_Clabough:
All right.
Mark_Miller:
Ed.
Shawn_Clabough:
We'll catch everybody else on the next episode of Adventures in.net.
Layered API Security with Ted Spence - .NET 143
0:00
Playback Speed: