STEVE_EDWARDS: To do my best AJ. Yo, yo, yo, coming at you live from my room with my space scooter in Oregon.
CHARLES MAX_WOOD: I'm Charles Max Wood from Top End Devs. Um, and I am doing a bit of work on the website so that you can get at some of the courses we're putting together. Um, so go check that out. And then we also have a special guest and that is, uh, Lars Eric. Rolled
STEVE_EDWARDS: I think is what I seeing here.
LARS ERIK_ROALD: Yes, that's correct. I'm from, uh, calling from Norway. Trondheim.
CHARLES MAX_WOOD: Oh cool.
LARS ERIK_ROALD: You can, it's the last time it's just like the writer. Roald Dahl. You know?
CHARLES MAX_WOOD: Yep. It's funny because my wife's actually watching Matilda the musical in the other room.
LARS ERIK_ROALD: Okay.
CHARLES MAX_WOOD: Matilda's one of the books that he wrote.
STEVE_EDWARDS: Oh, gotcha.
CHARLES MAX_WOOD: Roald Dahl is the author of Matilda. So, yeah. And if you've read the book, it's a fun book. So, but yeah.
STEVE_EDWARDS: Very cool.
CHARLES MAX_WOOD: Hey folks, this is Charles Maxwood. I've been talking to a whole bunch of people that want to update their resume and find a better job. And I figure, well, why not just share my resume? So if you go to topendevs.com slash resume, enter your name and email address, then you'll get a copy of the resume that I use, that I've used through freelancing, through most of my career, as I've kind of refined it and tweaked it to get me the jobs that I want. Like I said, topendevs.com slash resume will get you that. And you can just kind of use the formatting it comes in Word and pages, formats, and you can just fill it in from there. So do you want to tell us a little bit about yourself, who you are, what your background is, why people love what you do, or why they should give you money?
LARS ERIK_ROALD: I started as a software developer in 2001. And I started out with C Sharp. And then I got into Termscripts and Node. And today, I'm mostly working in Node and in Vue. So the last two years, I changed my workplace. So now I'm working with a company called SysTour in Norway. And we are maintaining this we're working for the Norwegian Public Roads Administration, keeping track of the vehicle toll passages. So, taking pictures of the cars and keeping track of all the passages. And we also have this fitment calculator for the Norwegian farmers. But the first 19 years I spent coding for logistics solutions for transport planning and a driver's app and track and chase and so on. Yeah.
CHARLES MAX_WOOD: Very cool. Yeah. I have a number of friends that have done logistics. My last contract was logistics. It seems like that's an area that won't ever go anywhere. Plenty of work to do there.
LARS ERIK_ROALD: Yeah.
CHARLES MAX_WOOD: Um, yeah, we brought you on today to talk about ORMs or Object Relational Mappers and your RDB ORM. Do you want to give us just a little bit of background as to why you created RDB and what problems it solves for you? LARS ERIK_ROALD: I think it started in 2014. We were given the task to create a new product. It was a booking website for the for the customers of the transporters. So we were free to decide which database to choose and also which technology to use. So we quickly found out that Postgres was a good choice. But we were eager to try Node as well. And at that time, I think Node was quite young. I think it was on the 0.10 or something. So we thought we'd go for Node. And we checked out what kind of ORMs that existed. And we found SQLize. And we considered using it. But the it has. It had so many bugs and was so inconsistent. So we knew we had to live with this project for many years. So we just considered making our own ORM. I previously had some experience with an in-house ORM in C Sharp. So it wasn't all that new. So that's how it started.
CHARLES MAX_WOOD: Very cool. So you built this product and you built an ORM to go on top of it so that you could map your database entities or database rows into objects in your applications so you could build it.
LARS ERIK_ROALD: And there are some ORMs like Prisma and Ristle ORM that does this today as well.
CHARLES MAX_WOOD: Okay. So I'm kind of curious because I remember way back early in my career, of course I was writing Ruby on Rails, but one of my mentors mentioned to me that I ought to write an ORM as an exercise to understand some of the things that go into building applications like what I was building, right? So Ruby on Rails has an ORM, it's called Active Record. But you see these in a lot of other systems. And it seems like on the face of it, it would be pretty simple, right? You do the query and you do the mapping. But as I kinda got into it, I figured out that there were a lot of other things that went into it, especially if you wanted to your.. Because you want your ORM to save you work, right? To do things for you. Like you mentioned the generative, what was it? Generative type generation or
LARS ERIK_ROALD: generic type mapping.
LARS ERIK_ROALD: But generic type mapping, yes.
LARS ERIK_ROALD: I was, we needed to have some kind of persistence without too much work and in the beginning we made it with the persistence ignorance. That means that you on the top of it you make some kind of not transaction but i don't some async local context around the whole thing. So that when you update anything on the record, you wouldn't need to call explicitly called save or something with just,
CHARLES MAX_WOOD: okay.
LARS ERIK_ROALD: When the transaction was over, it was commit. It just committed anything. Or if the database you needed to fetch anything, if you had pending changes, it would just commit it. Yeah. That was in the original implementation and it's still running in the core but as I made this TypeScript thing on top of it I found out that it would just be more beneficial to have explicit saving and so on because I also wanted to work in the browser as well so it was more convenient. And when you create your own ORM you will dig into problems like performance, especially when you're dealing with one-to-many relations and even nested one-to-many relations. So that's a typical problem that you will stumble upon.
STEVE_EDWARDS: Before we get going too far, could we step back and do a little basic definition of what we're doing here with an ORM and what it stands for? Obviously it stands for Object Relational Mapper, but from a database standpoint, what is the purpose of this? What is it doing for you? And how does it work just from a basic level? I know we've had, I've had the Larry Karpov on a couple of times to talk about Mongoose, which is the node for Mongo, which is a document database. So it's going to be a little different than a SQL style database. So what is it doing for the developer?
LARS ERIK_ROALD: Well, the ORM is just an object relational mapper. So it's a technique for converting data from a relational database to the structures in the programming language. And it's funny you say Mongoose because it's not relational. So, yeah, but it still has its usages because you have to create queries and so on. And that's, it's complex to do that. So, and usually, and you have a relational map, it's usually cut categorized into two or three patterns. It's the active record, which means that you define a modeling code and then you call me get one or get many and you get the records and these records also have behavior like they have methods like save or delete or something so they have some kind of logic they're not anemic and then there is the other category which is the the autumn data map pattern or the repository pattern this means that the rows are completely non-behavioral. They have no logic at all. That's only data. So in order to fetch stuff and to save stuff, you need to use the repository for that.Yeah.
CHARLES MAX_WOOD: Yeah.
STEVE_EDWARDS: So basically what you're going to do is from a SQL standpoint, you're going to define your tables, right? In your ORM, you're going to find, okay, here's my users table. Here's my, you know, blog post table. Here's my image table, whatever it is you have and your particular fields, right? To your mapper so that when you want to make queries, it knows, okay, here's my fields available and then access them from your ORM instead of going directly to the database. Correct.
LARS ERIK_ROALD: Yeah, that's correct. And some of the ORMs, they can reverse engineer your database as well. So you can generate your model from the database. And you can also have great migrations. But I think that if you're trying to both define your models in code and you try to reverse engineer the database, you will end up in trouble. because it's like a, it's like a hard, uh, getting worse with con merch with conflicts. It's, it's really hard. And I see that a lot of ORMs, they have a lot of issues regarding, uh, reverse engineering, mixing with forward engineering. So, uh, yeah, that's troublesome.
CHARLES MAX_WOOD: So you said that already B has been around for like, what, like, uh, 10 years, nine years, something like that.
LARS ERIK_ROALD: It's been since 2014, yes.
CHARLES MAX_WOOD: Right. That's correct. So yeah, I mean, I see a lot of stuff in here, you know, in the API mapping tables, connecting, inserting, fetching, updating, deleting. You can run it in the browser, which is cool.
LARS ERIK_ROALD: Yeah. It's scary, maybe.
CHARLES MAX_WOOD: Maybe. Transactions, which makes sense if you're on Postgres, or MySQL also has it. SQLite, I think, has it as well data types, default values, that'll make sense. You've built validation in, which is cool. You can do raw SQL. So I think these are a lot of things that people who work on a relational database would expect. Right, it makes sense to have them. So I kind of want to just move along a little bit and talk about the generic type mapping. So you mentioned that, you know, it's the way that you're basically getting your types into TypeScript, right? So how does that work? Like what does it do? Because I mean, I've fiddled with TypeScript, but this isn't something that I've had to deal with and it sounds like it's something that as a casual user it's not something that I would have to deal with, right? It's something that I would expect my framework or what have you to do for me, right?
CHARLES MAX_WOOD: Does it, so let's say I generate a model or an entity, right? Something that maps something out of my database into my application, right? Is this automatically generating the type in the d.ts file?
LARS ERIK_ROALD: Let's say you're creating, you map an order table with the order ID. You say table is called order or underscore ordered and you say column and you give the column name like ID and you give it the order number or a date. And then straight away you can call methods on this newly generated order which gets many and so on. And you can start using filters. Okay. We'd like to have order or the dates between from to. And it was a little. All these things will be created for you without no code can range at all, but you still have to implement it. Okay.
CHARLES MAX_WOOD: So, yeah, so I don't have to write any code and you're saying it's not generating any code. So how does TypeScript, because there's usually a language service behind TypeScript, right? So if I'm working in VS code, like it picks up my types and it does the mapping and it's smart enough to figure out you know, a lot of these pieces for me. So is there some dynamic thing that you're handing off to TypeScript? Or are you updating the definitions file, or how does that work?
LARS ERIK_ROALD: No, there's no definition files.
CHARLES MAX_WOOD: Okay.
LARS ERIK_ROALD: It's just, everything is just dynamic there,
CHARLES MAX_WOOD: so okay, so it's just an API to my TypeScript runtime, and you say, hey, there's this type, or there are these types, if I have several of them.
LARS ERIK_ROALD: Yeah.
STEVE_EDWARDS: Oh, that's cool.
LARS ERIK_ROALD: Yeah. It's really cool.
CHARLES MAX_WOOD: So what happens at the build step then does it, it just pulls it. It does the same thing basically.
CHARLES MAX_WOOD: Okay.
LARS ERIK_ROALD: So this generic type mapping is just it's just that.
CHARLES MAX_WOOD: Right. Well, I know a lot of people that are building apps with TypeScript. And so they would expect, for example, if I'm making a call to get all the orders out of the system and then I'm getting, you know, it gets all the line items for the orders, then they're expecting the get line items call to return a set of line item types that are defined in TypeScript. And so.. So all of this is just dynamically handled.
LARS ERIK_ROALD: Yes. You would use the fetching strategy. Let's say I have order with the lines on each line of products. So if you say, okay, orders get many. And with no fetching strategy, I just get the orders without lines and I can pass in a fetching strategy and everything that you pass this in in the fetching strategy is you have IntelliSense on it. So you could suggest, okay, I want to have orders, now I want to have lines. You can also say that, okay, I only want the product name. I don't want all the other stuff.
Are you under increasing pressure to ship code faster than ever before? Then it's time to work smarter with Raygun Perfo;rmance and error monitoring tools. Raygun gives you instant visibility into the health of your software. What makes it so unique is that it not only tells you when something's gone wrong, but it shows you exactly where it's gone wrong and how to fix it right down to the line of code. Made by developers for developers, Raygon Suite of monitoring tools are used and loved by thousands of software teams every day. Monitor your entire tech stack with widespread language support and native integrations with Microsoft Teams, GitHub, Jira, Slack, Bitbucket, Octopus Deploy, and more for even greater visibility. Visit raygon.com to resolve issues faster and to deliver flawless digital experiences for your users. That's raygon.com to get started with your 14-day free trial.
CHARLES MAX_WOOD: Very cool. So then the generic type mapping does the part where it says, and I expect these to be line items or expect these to be products or expect these to be strings that are product names or whatever.
LARS ERIK_ROALD: Yes. I say if you would have some code generation, but there isn't any.
CHARLES MAX_WOOD: Right. That's slick.
LARS ERIK_ROALD: Yeah.
CHARLES MAX_WOOD: So you said it was a lot of work. What did you have to do to make this work?
LARS ERIK_ROALD: Well, in the there's a lot of generics involved and it's kind of meta programming. I don't know what to call it. And also you cannot do any counting in generics. So let's say I have a composite idea with two columns in it. And then I want to have a... Okay. His table with this composite idea. I want to join it to another table. And then I wanted the TypeScript to suggest the correct types based on the from table and the primary keys of the target table. So they would match suggest based on the primary keys. But the problem is that I would always suggest that you have two inputs first key and the second, the first column and the second column. And this is really hard because you can't do any counting. You can, you have to tell the, the generic stuff that one plus one is two. There is no way of just saying that. Okay. I have to. Yeah. So I, I, I stopped at six, six columns in the, in the composite case. I think that's, that's sufficient. So that's simple things are hard and other things are easier. So it's.
LARS ERIK_ROALD: Yeah, it will. So the only reason is to, to have a better developer experience. And I think that if you want your ORM to be public and people want to use it, you just have to have TypeScript today. Otherwise you wouldn't survive, I think.
CHARLES MAX_WOOD: Yeah, there seems to be a lot of adoption for TypeScript, so that makes sense.
LARS ERIK_ROALD: But using TypeScript for the library itself, it was not really an option. It would be very difficult. And all the the tops would be in the way, I think. But being a consumer of, of honor, it's nice, but writing the ORM with, with TypeScript, that that's a lot.
CHARLES MAX_WOOD: Oh, really?
CHARLES MAX_WOOD: Steve do you have some questions you want to ask? I feel like I've kind of been monopolizing the last 20 minutes.
STEVE_EDWARDS: No not really I'm not haven't dived into the TypeScript world yet. You know, I think I've discussed here most of my experience with no RM is coming from Laravel. Um, eloquent since that's the RM for eloquent is the RM for Laravel. So I'm fairly, you know, familiar with how they work and what they do, but not in the TypeScript world. So I've mostly in PHP as compared to node. So that was just my reason for asking for the.. You know, basically I finished and I would have known where it is. But no, I can't think of any other questions off top of my head or off hand.
CHARLES MAX_WOOD: Well, I'll keep going then. Um, so Lars, I'm wondering if you're using RDB on any apps that you're building currently.
LARS ERIK_ROALD: Yes, it's still being used at the previous coworker, the previous workplace. And, uh, I think that you're using it in about hundred installations. So it's, it's not the widely used, but it's used in critical systems. And it's also used at my current employer. I think it's, it's, yeah, we made an IPA for receiving the status of the each toll at the toll station. Yeah. Yeah. I created a rest IPA for every fifth minute there is some statuses from the cameras, radars and lasers from all over Norway to this API. And this is using the RDB.
CHARLES MAX_WOOD: Very cool. And there is all that written in TypeScript now or?
LARS ERIK_ROALD: Yeah, that's, that's touch it in the, yeah.
CHARLES MAX_WOOD: So, you know, you mentioned earlier that performance is always an issue. So how well does this perform? How many API calls can it get over what time period and how much computer you put behind it?
LARS ERIK_ROALD: That the person, your machine, of course, but I haven't done in a benchmarks on it. It's pretty performant. That's all I can say. We have this hub at my previous employer that receives all the truck data from the drivers from about 150 companies at the same time. So it's performing well. I did a special query for it because it has this built in for JSON queries. So you can create one large query with the subqueries below it benefit and it performs extremely well. I don't know if you have tried it. It's called for JSON also in SQL Server. So if you name your columns and correct it. You could get a hierarchical structure back from the database. So you don't need to do many queries and round shapes. But that is special made for SQL Server. So what I do for the other ones is that, let's say you have a fetching strategy with order and order lines, that will use two queries to first get the order and then the lines. And if you have regular joins, like order with customer, then this will perform in the first query. So only one to many queries will give an extra database run trip. And I also tried to make a pretty slick filtering mechanism. If you, it's a bit like a link expression since C sharp, you know, where you can say order dot lines and you have a callback and they say line dot product equals something. So it's.
This episode is sponsored by Miro. I've been working lately on a whole bunch of different projects for top end devs. Everything from meetups to courses to podcasts to the website. And a lot of the things that I've been working on have gotten really, really messy. And even though I have some software that helps me manage the projects, one thing that I figured out was that it really helps to have a tool that you can use to manage kind of the layout and the organization of your project, not just whether or not tasks are being done. And so I picked up a program called Miro and Miro, what it does, I'll just give you an example. When I'm building a course, I sit down and I open up their mind map and I just put all of the information into the mind map. And I'll do that for probably anywhere from 15 minutes to an hour, just depending on what it is that I'm organizing. And then what I can do is from there, I can actually reorganize it and rejigger it so that it actually makes sense. And it really, really helps me get all of the ideas on paper or on the screen in this case and get an idea around how these things come together. So if you're looking to put together a project or organize some ideas, you should definitely check out Miro. You can find simplicity in your most complex projects with Miro. Your first three Miro boards are free when you sign up today at Miro.com slash podcast, that's three free boards at Miro.com slash podcast.
LARS ERIK_ROALD: They're like C-sharp link expressions. So that's really nice and convenient, I think.
CHARLES MAX_WOOD: Cool. Yeah, what you're talking about with, you know, making the second round trip through the related objects. Rails does that as well. You can tell it to include those in it. Instead of making, we've been talking about this, you know, since Rails came out in what, 2006, is N plus one queries, where you... You make the first query for the object and then you make N queries to fetch the rest of them. Right. So you have 20, uh, line line items. And so you make 20 queries to get them all. And so you reduce that to making one query that gives you 20 rows or 20, uh, entities that you can translate over. And yeah, it sounds like you're solving the same problem in a lot of the same ways.
LARS ERIK_ROALD: I know that some of the ORMs have laser loading as well. So.. So if you initially only got the lines without the orders, without the lines, you can say that, okay, I want the lines as well, and you will get it. But then you need to call some method or await the promise or something.
CHARLES MAX_WOOD: Yeah. Effectively, the way that Rails does it is you tell it when you fetch the parent entity that you also want to include the child entities. And so it... It basically eager loads it, right? This sort of lazy loads it. And then since it already has it in memory, then when you say, okay, now I want to say iterate over them, right? It already has them in memory. So it pulls them from memory instead of from the database. That's faster.
STEVE_EDWARDS: So is there anything new coming for RDB?
LARS ERIK_ROALD: Well, I just added the conflict handling so you can move on the insert and updates.
LARS ERIK_ROALD: So on insert, it would typically be like an up search or something. And you can, you can apply a strategy saying that, okay, for this column, if there's a conflict, I want to just skip it. And if on this column, I want you to override the existing one. And yeah, so there are three strategies. It's the default, the optimistic just update it unless it's already there. And the overwrite and the ignore or skip on conflict, I call it. And you can also have this on it's also checking the old values. So let's say you are in the browser and you're fetching the rows from them from back end. And someone changed the row in the meantime. Then you can have a strategy to saying that, okay, if this row, if this column was changed in meantime, then you can just overwrite it or you can skip it or you can have a conflict, which is the default. So I created this conflict handling for all databases for SQLite, SQL Server, MySQL, and PostgreSQL. That's the latest addition. And I'm planning on implementing it, implementing for Oracle as well.
CHARLES MAX_WOOD: Cool.
CHARLES MAX_WOOD: Cool, very cool. Well, if people want to check this out or if this is interesting to them, what are the best places to go look at it? I think I'll post the GitHub link and the website link, but are there other places that you want, or that you want people to go to kind of get updates and stuff?
LARS ERIK_ROALD: No, I think GitHub is just fine. I have only the web page and the GitHub page. Just want to keep it simple.
CHARLES MAX_WOOD: Right.
LARS ERIK_ROALD: And there are nice examples in the documentation. So it's really easy to get started. There's one thing that's missing and I'm considering adding it and that's migrations.
CHARLES MAX_WOOD: Oh yeah.
LARS ERIK_ROALD: I think that's something I would need to have but I'm a bit worried about the reverse engineering part because when I see all the issues in the other ORMs a lot of them are related to migrations and typically when you mix forward and reverse engineering, it will end up in conflicts. I think I'll start out with just forward. You just define your models and call some.. just call generate or something and that will generate as a migration and then you can just deploy it and then change the data model again and call generate and then you will have a new migration.
CHARLES MAX_WOOD: Yeah, Rails Active Record has migrations and I use them pretty heavily. So in fact, that's how you manage all of your database structure. Right. There's, there's not really, uh, you know, a better way to manage your database structure than that for, for those apps. Um, how do you do it currently? Do you have to have your database specialist or your developers go in and directly run the queries to update the database, or do you have another mechanism for that right now?
LARS ERIK_ROALD: Uh, we just, at my current workplace, we use, uh, liquid base actually but this is mainly because it's working for Java and other languages as well. So that's our tool.
CHARLES MAX_WOOD: Okay.
LARS ERIK_ROALD: I'm not sure if you have heard about it, EquoBase?
CHARLES MAX_WOOD: Nope, but I'll post a link because I just looked it up.
LARS ERIK_ROALD: But historically, we just use plain scripts. I just name them. 001, Haskell, and just make a small script to run them sequentially. And then I have a table in the database that says the current version of the migration now. So pretty simple.
CHARLES MAX_WOOD: Yeah. That's more or less how Rails works. It's just, it's actually written in code instead of SQL. And yeah, they're the number on it is timestamped instead of, um, you know, version number, but it used to be version number. It used to be, oh, one or one, two, three, four, five, six, 10, whatever. Right. Yeah.
LARS ERIK_ROALD: But sometimes it, uh, migration can involve, uh, data migration as well. So you need to do something manually.
CHARLES MAX_WOOD: I generally try to do those in a separate task. Because if you're doing structural migrations, a lot of times you can't rely on the structure of the database to match up with your data migration. And you'll get errors saying, hey, this field doesn't exist or things like that, depending on how it runs. Because Rails isn't sequential, it keeps track of each migration and whether or not it's been run on your database.
LARS ERIK_ROALD: Okay. And so I think some ORMs you can generate migration and then you can add custom SQL before it and after it.
CHARLES MAX_WOOD: Yeah. Yeah, you can add custom SQL in there, but it's kind of ugly to do. So, and by ugly, it's just, it's not as clean as the change this column, change this table kind of thing that you get out of the nice rails DSL or domain specific language they have for your migrations. So, but yeah, it makes sense to have them. I like that. All right, well.
LARS ERIK_ROALD: You can also, when you can write filters, you can also mix them with custom filters, like with raw SQL filters.
CHARLES MAX_WOOD: Right.
LARS ERIK_ROALD: And you can have, you have this logical operator, you can have and, or, you can just mix as you like. Sometimes it's easier to write a filter directly in raw SQL than using the ORM.
CHARLES MAX_WOOD: Yeah, that's true of pretty much every other system I've worked on as well is just that yeah having that possibility because yeah there's not a clean way to get it using the ORM's language around looking stuff up.
STEVE_EDWARDS: So when you talk about a filter, you're talking like a where condition?
LARS ERIK_ROALD: Yeah.
STEVE_EDWARDS: On a state back. Okay. So yeah, one of the things that eloquent does in letter bell that I think is really cool is they have something called scopes. And a scope is where you define a separate function, you know, that says, you know, you feed in your query builder and you say, here's my condition for whatever this where you're supposed to do. Right. And then when you're actually building your query, you just chain it on. So everything's chained, you know, you're using the fluent approach where you chain things on. So if you just want, if you're writing your query, you know, where this and this, then you just chain on your predefined scope and that function does a filter. So you could dynamically add it or you could, you know, hard code it depending on how you're writing your code. But it sounds like that's what you're, you're dealing with here is the ability to have a custom defined scope that you can just, you know, add as needed.
LARS ERIK_ROALD: Yeah.
CHARLES MAX_WOOD: It looks like filter can go in. Yeah. And contain a where value. Anyway, definitely interesting. And I don't think there's really a, maybe there isn't, I just don't know about it, but typically we're using wear conditions and not filters in our rail stuff. So that might be an interesting way to solve at least one particular problem that I'm running into right now on my client project. Anyway, but scopes don't use the filter keyword in SQL. They actually just encapsulate a where or...
STEVE_EDWARDS: A where, correct.
CHARLES MAX_WOOD: Yeah.
CHARLES MAX_WOOD: Anyway, cool, cool stuff. All right. Well, I'm going to push this into our picks.
Hey, this is Charles Maxwood. I just wanted to talk really briefly about the Top End Devs membership and let you know what we've got coming up this month. So in February, we have a whole bunch of workshops that we're providing to members. You can go sign up at topendevs.com slash sign up. If you do, you're going to get access to our book club. We're reading Docker deep dive and we're gonna be going into Docker and how to use it and things like that. We also have workshops on the following topics and I'm just gonna dive in and talk about what they are real quick. First, it's how to negotiate a raise. I've talked to a lot of people that they're not necessarily keen on leaving their job, but at the same time, they also want to make more money. And so we're gonna talk about the different ways that you can approach talking to your boss or HR or whoever about getting that raise that you want. And... having it support the lifestyle you want. Uh, that one's going to be on, uh, February 7th, February 9th. We're going to have a career freedom mastermind. Basically you show up, you talk about what's holding you back, what you dream about doing in your career, all of that kind of stuff. And then we're going to actually brainstorm together. You and whoever else is there and I, all, all of us are going to brainstorm on how you can get ahead. Um, the next week on the 14th, we're going to talk about how to grow from junior developer to senior developer, the kinds of things you need to be doing, how to do them, that kind of a thing. On the 16th, we're gonna do a Visual Studio or VS Code tips and tricks. On the 21st, we're gonna talk about how to build a software course. And on the 23rd, we're gonna talk about how to go freelance. And then finally, on February 28th, we're gonna talk about how to set up a YouTube channel. So those are the meetups that we're gonna have along with the book club. And I hope to see you there. That's going to be at top end devs.com slash sign up.
STEVE_EDWARDS: Well, well, well. So We'll get to the dad jokes of the week, which are the highlight of any episode. If you ask me, but that's only if you ask me. Um, now for those who are fans of the dad jokes, I must, uh, tell you that I have lost my drum roll, my drum rim shot sound effect, at least for now, I'm hoping to get it back with changeover.
CHARLES MAX_WOOD: Yeah. We switched systems, but there is, uh, they showed me how to do sound effects, but yeah, we're going to have to upload them and I don't. I haven't figured out how to do that.
STEVE_EDWARDS: So I will have some fun. So when I tell the jokes, just imagine that the normal rim shot in your head or, or maybe Chuck can give me one. Um, so thank you. Thank you. Yes. So man walks into a bar, you know, holding a golf club and the bartender looks at him and with this look and says, why the golf club? And he said, it's my designated driver. Um, I was talking to my wife, I was talking to my wife the other day and, and she, uh, I, I told her that I absolutely love, I think you say this Worcestershire sauce. Um, and she asked what's so special about it. And I said, it's hard to say. Right. And then, uh, we were out at, uh, uh, uh, wedding here recently. And, and she said to me, you know, that's the fourth time you've gone back for dessert. Doesn't that embarrass you? Say no, I just tell them it's for you So those are the dad jokes of the week.
CHARLES MAX_WOOD: Awesome. All right. Well, I'm going to do more traditional picks. I always shout out about stuff. Um, it is that time of year again, uh, friend of mine owns a board game shop. I do this every year. I swear. I'm telling you, this is why I'm learning these games. So I'm going to do some picks of some board games. I try and pick a new board game every week. I'm not trying a new board game every week often. So, you know, sometimes I'm doing repeats, but for the next five or six weeks, I'm volunteering at Tim Con, which is a local game board game conference here in in Utah. It's in Provo. And anyway, you can bring your own games and then you know, just have little signs you can put up and say, Hey, new players welcome, right. So I like doing that. Just walking over. Hey, this looks interesting. Can I play right? And so then we'll play around of whatever game somebody brought or they have a library where you can go check games out and, uh, some, somebody like brings in his whole game collection, the guy that runs it and so you can borrow a game from him basically. Um, and then, but volunteering there specifically, I'm volunteering with gamers in, which is a local game store. And, um, they, so that that's the game store that my friend's part owner in. And what they do is they demonstrate six games that are fairly popular right now. And, uh, so people can sit down and play them. And usually he tries to pick games that you can play in less than a half hour to an hour. So, um, this one was one of my favorites of the ones that we tried out. It's called Acropolis. That's A-K Acropolis, A-K-R-O-P-O-L-I-S. Um, and it runs about 30 minutes. Uh, the way that it works is really simple. In fact, uh, board game geek puts a weight on it of 1.79. So I tell people that a two on board game geek is kind of a casual gamer that you know, people who aren't deep into board games and mechanics and strategies can play without, you know, and still have a shot at winning it and we'll enjoy it, right? They're not trying to overthink all the things. And so what you do is you.. You have different color tiles, so they're hexes, and you get a piece that has three hexes on it, right? And so you typically start with one piece that has a blue hex on it that is a star hex. And so the number of stars you have in a color is the multiplier for the other tiles that you have that are properly placed on the board that are that color, right? So then if you put a blue tile out, and you have one star in blue, then they're all worth one. If you have four stars in blue, then all the blue tiles that are properly placed are, you know, four points instead of one. Right. So, um, the way that we played it, and I can't remember if there are different scorecards, but the scorecard we played with, um, it was, uh, in your biggest group of blues, each one counted for a point, right. And then your stars made a multiply up. Uh, yellows were yellows that weren't next to other yellows. Um, reds were on the edge, uh, purples were completely surrounded. And I think there was another color greens. You just got a point for every green one you had. Right. And then you, what you do is you have the number of people who are playing plus one tiles out there. And, um, the first person takes one and the next person takes one. The next person takes one and whoever goes first, they put the token that lets somebody else go first onto the first tile after they take one. Right. So they, they give that up every time, but if nobody picks it up and they get it back is the way that it works. Um, but typically somebody is going to pick up that tile cause they want to go first and there isn't anything else out there that really appeals to them, but you can build your city, um, horizontally, right? So you can fit it in around the other pieces or you can stack them. And so if you put it on top of three other hexes that are already in your city, then those spots count for two or if it's three levels up three, right? But you have to be able to cover up three other spots that are connected in the lower level, right? So you can't have an empty space underneath any of them. But yeah, so you just build it up and then you score your city based on those stars and the number of tiles you have that fit the description and whoever has the most points wins. So.. It's, you know, it's a relatively simple game, but there are a lot of ways to win and it really isn't heavily skewed toward any one of them. So it kind of depends on what you're able to pick up and things like that. So you do pay attention to everybody else just in the sense of if he picks that up, that's worth eight points to him. If I picked it up, it's worth four points to me and the other tile is worth five points to me. So he wouldn't get as far as head if I steal it, but mostly you're just trying to build your own city, right? So that's as complicated as it gets. Uh, like I said, runs about a half hour. We've played it with three and four players and that seems to go pretty well. Um, it says age eight plus. It is like, I mean, I described it. It's that simple. So yeah, you can definitely play it with eight year olds. Um, I'm going to put an Amazon affiliate link in here as well. So if you buy it, I get a percentage, doesn't make it cost anymore for you, but I feel like you know, you have to disclose that, but yeah, if you want to go buy it on Amazon, definitely go pick it up right now. It's listed at, uh, $28 and you can get it overnight, uh, right now if you have Amazon prime. So anyway, super fun game, really, really enjoyed it. And so Acropolis is that pick. Um, the other thing I'm going to pick is, um, so folks, I don't know if I've talked a ton about this, but my goal next year is to complete a half iron man. And so if you're, if you're wondering what that is, it's a triathlon. Um, it's about a mile and a quarter swim, uh, 56, 57 mile bike ride, and then a half marathon or 13.1 miles at the end. Um, of the, you know, to finish it out. And so obviously I have to get in shape. Um, and getting in shape isn't as simple as just being, you know, being fit enough to do that stuff, but you have to have, you know, the right muscle set groups exercise together for the bike, for the swim and for the run. So right now I'm getting back into swimming shape. I've been mostly focused on running. That helps your cardiovascular system and stuff like that be able to support the rest of it. But then I get into the pool and I get out and I'm sore or tired because I haven't worked some of those muscle groups in a while because I quit exercising for like four months. So I'm trying to get back on the wagon and I'm trying to get trained up. One of the things I bought and this is for the swimming. So if you're not aware, if you've never tried it, I have a couple of sets of waterproof headphones that are Bluetooth, but Bluetooth won't work more than like an inch underwater. And so as I'm swimming down the pool, I mean the second I go under the water, it cuts out, right? So I either have to store the music or whatever on my headphones, or I have to have something that'll transmit it and I like to play whatever on my phone without having to worry about loading up my headset. So I bought this headphone set called Zygo. And what it does is it has a base unit that pairs with your phone, right? So it does the Bluetooth. And I just set mine right next to my phone. And then it has a headset that connects to the base unit. And that headset actually connects to the base unit through an FM transmitter. Right. So it's a low powered FM transmitter and FM radio waves will penetrate water. Right. So I can swim up and down and it mostly sounds pretty good. And then it's phone conduction, headphones, and I swim with air plugs anyway. So that works great. So anyway, long winded pick, I'm picking Zygo. I really, really like them. Makes my swim a little bit more enjoyable because I can listen to music or a podcast or something while I'm swimming. And so I'm going to pick that. And then, there was something else I was going to pick and I'm blanking on it. So, uh,
STEVE_EDWARDS: well, while you're thinking about that, Chuck, as a former competitive swimmer and water polo player, uh, I thought it'd be worth pointing out that when you swim, you're supposed to stay on top of the water, not necessarily below the water. Uh, you know, just saying.
CHARLES MAX_WOOD: Yeah, fair enough. But, uh, my, my head is far enough into the water to where the Bluetooth just doesn't work. So,
STEVE_EDWARDS: I gotcha.
CHARLES MAX_WOOD: Okay. Right. I mean, if you swim with your head down, it puts the headphones, you know, an inch or two in and that's enough for it to disrupt the Bluetooth. So anyway, um, so I'm pretty happy with those headphones and, uh, yeah. Um, the other thing I'm going to pick, I guess, getting back into this is tri dot. So tri dot T R I D O T. Um, it gives me all of my, um, workouts for my triathlon and, uh, So anyway, as you do assessments periodically, and then it adjusts the intent, it tells you what intensity to do the workouts at. And I really like it, it works out really, really nicely. So.
STEVE_EDWARDS: So do you have a particular triathlon that you're shooting for, or just, you wanna do one at some point down the road?
CHARLES MAX_WOOD: So the one I want to do, I don't know if I'm gonna be fit enough to do it, is in May. And it is the St. George, uh, half.
STEVE_EDWARDS: Oh, way down there, huh? That'd be nice and toasty.
CHARLES MAX_WOOD: Yeah. And like I said, it's in May, so it'll be warm. Um, but it's, it's close enough to where I can put on my gear of my truck and drive down, right? So I just stay there the night before and then go and yeah, I really. I'm really digging it. So anyway, the other thing that's nice about Tridot is that it connects to, uh, Garmin. So my watch and things like that. So it'll import. When I go do the workout, it'll import all the data, um, and do all the other stuff. So, um, anyway, really like Tridot. All right, Lars, what are your picks?
LARS ERIK_ROALD: I have a couple. I have tried out this platform for building HTML hybrid apps. So what it does for you is that you can develop locally. And it's integrated with GitHub. And it will support different Cordova plugins. And it can actually build the native app for both App Store and Google Play. And it can deploy directly to App Store and Google Play. So you don't really need a Mac to.. to deploy. So normally, if you want to deploy it after, you have to have a Mac. So this is a great tool for teams as well, I think. And I have a great debugger app. So you can just download the debugger app and just push your code. And it will download the code into the debugger app. And you can run it and check how it works they do that.
CHARLES MAX_WOOD: Oh, nice.
LARS ERIK_ROALD: And it's, uh, it's called the Monaca. I think it's a Japanese company or something.
CHARLES MAX_WOOD: Uh huh.
LARS ERIK_ROALD: Monaca.io they also have a UI library, but don't use it. That's not the good part. But I think I've tried out something called Framework 7. It's made by someone called Vladimir Kharlampidi. So it's free and it's open source, and it has a native look and feel. So it's framework7.io.
CHARLES MAX_WOOD: Cool.
LARS ERIK_ROALD: Yeah. And I have a, I think I have a couple of jokes.
CHARLES MAX_WOOD: Oh, go for it.
LARS ERIK_ROALD: Yeah. This one is a functional programming joke. Two funks went into a bar. Nothing happened. And the second one, why do Java programmers have to wear glasses because they don't see sharp?
STEVE_EDWARDS: Oh. Oh, that's a little bit language specific one there.
LARS ERIK_ROALD: Yeah.
CHARLES MAX_WOOD: All right. Good deal. Well, Lars, if people want to find you say on social media, follow you on Twitter or GitHub or whatever. Where are you?
LARS ERIK_ROALD: I'm usually on Twitter. So it's L R O A L.
CHARLES MAX_WOOD: All right. We'll put that in the comments here and into the show notes. Um, thanks for coming. This was really fun.
LARS ERIK_ROALD: Thank you. Thanks for having me.
CHARLES MAX_WOOD: Right. We'll go ahead and wrap it up here. Until next time, folks, Max out.