CHRIS: Are you there? AMOS: Hello, I’m here. How are you? CHRIS: I’m good. Hang on. I need to shut my door and tell my family to shut up. AMOS: Perfect… Welcome back. I totally think that all that silence should be the beginning of the show. CHRIS: That could just be the show. AMOS: It might be. I’m a little exhausted this morning. CHRIS: This is in keeping with my theory of ‘I should talk less’. AMOS: Ah, no. It was just funny whenever my assistant was doing the transcript of the podcast and there was one that you and Anna were on, and she’s like ‘It’s 913 words. It’s a whole page!’. I said, ‘well, it was probably important’. But I still had to give you a hard time. CHRIS: I don’t know what you expected when you were like ‘we’re gonna start a podcast’. It’s like, have you not met me? Do you not know what you’re getting into? AMOS: It’s a podcast. We’re supposed to talk. CHRIS: Right. I think the theory is that we all talk the same amount. That hasn’t happened though. AMOS: Well, why not? You can do all the heavy lifting and I can just be quiet. And then if you say something wrong, I can be like ‘that was Chris, not me’.... So, how’s your week been going? CHRIS: Oh… fine. I dunno. It’s the summer. Well. The summer has been harder than ever before just because of working remote. And when I started working at Letote last year, that was the first time I was working remote all the time. And for the summer, when my kids were home, I’d go work downtown at an office. But that got to be kinda cost prohibitive and so I was like ‘oh, I’m spending a lot of money on this office. I don’t need to do this anymore’. And that just happened to coincide with my daughter going back to school and my son going back to school, but now I’m working from home still and not only is it summer, but the kids are older now and more engaged… It’s tough. It’s been a tough transition. We still haven’t settled into a schedule yet and it’s been a couple weeks now. Especially with starting a new job, it’s been tough to figure out how to get the most work done and try to find productive times during the day. AMOS: I find that with any time you start a new job, there’s this pressure to perform. That, after people know you and know what you can do, you don’t feel so bad. And then, having the kids. I rent an office about three miles from my house because of that. There’ve been a few times when I’ve been snowed in and the kids are snowed in and I have to put very strict boundaries and say ‘when this door is closed, Dad’s not home. He’s at the office’. But then I can hear ‘em and if they start to argue and my wife’s not fast enough to get to them - like she’s in the shower, then I feel like I need to run out there and take care of it. And I also can’t go to the kitchen without spending thirty minutes there, ‘cause I’ll just be like ‘oh, I’ll go ahead and wash these dishes real quick’. CHRIS: That’s the other thing - I’ll hear the unspoken cry for help that is my wife clearly at her wit’s end with this three year old and this five year old just yelling at each other. And I can’t help but just be like ‘I gotta go engage. I gotta at least go back her up ‘cause this is getting out of hand.’ And this happens a non-trivial amount of times throughout the day. AMOS: Work and parenting with Chris and Amos. CHRIS: Hashtag my Elixir status. AMOS: I have a problem with the summer time too - the office that I rent is really cheap. And comes with internet. And the internet that I get for free is only thirty dollars less than what I pay for the office. But that’s because everyone in the office uses it. But I use it way more than anybody. They’re like checking Facebook once in a while. But. I have no windows. And I’ll be in there and I’m like ‘oh, I know it’s a nice day today and I wish I could just look outside’. And then in the winter time, I get to work and it’s dark. And I leave and it’s dark. And I never see the sun. So. I try to realise that it’s lunch time and go outside. And that really helps me get reengaged. And sometimes in the summer, I’ll run home and have lunch with my kids. That helps me feel a little better about running away from ‘em all the time. CHRIS: It’s amazing the difference just going outside and taking a walk can do for my day. And I try to spend a pretty healthy amount of time outside. I’ll go chill in my hammock and think in there or work in code from there. But even just getting outside to take a walk can change the way I look at my day… You should invest in one of those happiness sun lamps that are only a couple hundred dollars on Amazon. Blast you with lumens to make you happy. Hang on. Let me see here. AMOS: You gonna look one up? CHRIS: Yeah, I’m gonna find you one. Happiness lamp… here we go. AMOS: I looked at one of those for waking up. ‘Cause I have a hard time waking up. And then I found out if I eat healthy and go to bed at the right time, I don’t have a hard time waking up at all. Like, I don’t need a lamp. I just need to be healthy. CHRIS: Verilux Happy Light. Full size. 10000 lux light therapy enegry lamp. It’s got four and a half stars. AMOS: So how much is it? CHRIS: It’s only a hundred dollars. AMOS: Nahhh. Nope. CHRIS: We’ve gone too far. Science has gone too far. AMOS: No, I have to spend a hundred dollars this weekend on a garbage disposal ‘cause ours is leaking. Sorry. Garbage disposal or lamp. I’m gonna have to go with the garbage disposal. That and my daughter broke her arm on Monday. CHRIS: Oh, yeah. That’s so sad. You sent me the X-Ray and it’s just horrifying. It’s like some Eldritch nonsense. It’s amazing. AMOS: I tried to talk her into letting me take a picture of it. I was like ‘you want a picture of this for later?’ like before they set it and put it in the cast and she’s like, ‘no, no, don’t take a picture. I don’t want to see it’. And then her X-Rays she didn’t want to see until after they straightened it out. CHRIS: It’s at a very precarious angle… It’s at a very very very very interesting angle here. AMOS: Yeah, I called my wife and I said ‘hey, uh, I’m at the piano teacher’s house and Miriam just broke her arm’ and my wife’s like ‘how do you know it’s broke?’. And I said ‘oh, it’s broke’. That’s all I could say. And I think she wondered why my daughter wasn’t screaming the background possibly. But she didn’t cry at all. It was pretty amazing. CHRIS: Was it just shock? AMOS: I think it may have been just her. She’s not a big crier or anything. She just kept it together. The biggest gripe and complain she had was she just wanted to sleep. Even on the way to the hospital. She was like ‘I’m tired now’. And she’s like. ‘Mom. Dad. Grandma. Be silent. I need to sleep.’ I was like ‘alright, boss’. CHRIS: Whatever you say. You’re the one whose arm is poking out at 90 degrees. You make the call. AMOS: Oh man. So. Ten minutes in. We’ve talked about working at home. I’ve kinda gotten the BSing out of the way. So… I have some other horror stories. My daughter was actually pretty easy aside from being up really late. But work has been pretty tough. I’ve been working on an email system that emails out alarms, and I put it all together, tested it, ran it locally. It works. Don’t really have integration level tests on it. Just this function translates this data test. And I push it and some other people push their changes and QA gets it and they send it back. The data format changed of how they do a recipient. So I fix that, push it back, then it’s messed up again. Somebody changed - we have this - it’s called a data dictionary file. It talks about the data and where it’s coming from. And it - well, somebody changed that file, so it quit calling the file that I expected to be called - and it was an accidental change too. But because there were no integration tests for this feature, that didn’t get caught. So… Integration tests. I think Dialyzer could have done some good there, ‘cause it could have said ‘hey, this data isn’t right anymore’. So I guess that’s been around a while. How do you go adding Dialyzer to it without having Dialyzer just hate you for the next six months while you add all the specks in. CHRIS: Oh boy. I don’t know. I’ve definitely tried to add Dialyzer after the fact and its a lot of work. Its often a lot of work not because of things you’ve done in your code. Does that make sense? I think most of the things that I end up going and trying to fix are dependencies. And the canonical example to me is - depending on what version of Phoenix and what version of Elixir and what version of Dialyzer etcetera and all these things - for a while, you would run Dialyzer in your Phoenix app, and depending on what you had set up in your dependencies, it would start throwing a bunch of no local returns from random templates and stuff. Have you experienced this? AMOS: Yeah, and I’ve used Dialyzer on other projects that were using it from the beginning, and that was easy. And beautiful. But yeah, I’ve tried to add it. I put specs in the code most of the time. Because we’re not using Dialyzer, I’ve backed off a bit. A lot of times, actually, when I don’t use it, it’s because I don’t have a good grasp on what the entire system coming in is. I have like a partial idea and I’m afraid to lie to somebody with the spec and say ‘it only deals with these two things’, when actually it might get these other three things that I don’t know about. CHRIS: It’s funny when you see that kind of stuff in the code. I’ve definitely seen places in code where the specs… they’re more based on hope than they are based on reality. AMOS: Like some code I’ve written. CHRIS: The way that you find this stuff - I always get the different wrappers confused around Dialyzer, but I think Dialyxer is the most common one. Is that the one you use? AMOS: Yeah. CHRIS: I’m trying to remember if that one runs with the ‘overspec’ flag on by default or not - but the overspec flag on Dialyzer is really helpful for that stuff, ‘cause it’ll actually tell you when you’ve over specified a function. It’ll actually say ‘your success type is actually any here, but you’re saying it should be a string. But you’ve not done anything that actually forces that to be a string’. AMOS: Oh, that’s pretty cool. CHRIS: One of the things that you don’t think about is - let’s say that you have function phoo. And function phoo you believe should take strings, right? Function phoo is called by function bar. And you pass arguments into function bar, and he passes it down into function phoo. Is this all clear as mud? AMOS: Yeah. CHRIS: And you’re like ‘okay, these should be strings’. Well, if you want function phoo to accept a string, and if you wanna spec it like that, at some point one of those two functions needs to define a guard clause that is binary. Or else the actual success type is any still. There’s nothing to actually force that to be the case until you limit the amount of things that you can pass into that function. And the overspec flag will find that kind of stuff for you. AMOS: Nice. So, it takes a little more of that hopeful specking out of it. That, at the edge of my system, I’ve said that this thing coming in should be a string, but if I don’t have a guard clause, someone could pass me something else and I wouldn’t be any wiser until it’s too late. CHRIS: I’m not sure what Dialyzer would have done with your problem. Because your problem has to do with the kind of data you’re passing through the system, and realistically, that’s more of a business logic than it is types… I dunno. You would know more than me…. AMOS: So what I had come in originally was an atom and they changed it to a tuple. And that was on my suggestion that we should look at changing it, but then they just did it. After I’d written for the old way. So I got in there and the data would have a - it was an atom, but it would be - maybe it was a string. They kinda conflate the two a lot. They being me too. The project in general. It had like a word followed by a number, and that number was an index into an ets table, and the word was like a key to get into the ets table, so I had to spit it apart and get - it was like a sheep man’s foreign reference. And I told them to change it to a tuple with a number at the beginning and an atom as the second part of it so that there wouldn’t be that weird code. And they were like ‘yeah. Why didn’t we think of that?’ and I was like ‘I just thought why am I parcing this?’ And so that’s what changed. And so I was saying that it should be a string or an atom or whatever, and they were sending a tuple, Dialyzer would have noticed that. CHRIS: Yeah, I think that sounds like the kind of thing that Dialyzer probably would have caught and saved you a bit of aggravation there… And to your original question of ‘how do you add Dialyzer to a project, man, I don’t know. That one’s tough because Dialyzer is - it’s a very useful tool. And for the most part, I think we should all be using it. For the most part. I mean, I think it hits the sweet spot when it comes to types for me. I’m a big Haskell fan, and I’m a big Rust fan, but I also am of the opinion that types only do so much for you. They solve a class of problems that I’m not overly concerned about solving all that often. And they solve a class of problems that are more useful to solve when you have a team of some arbitrary size that I couldn't begin to quantify because at the end of the day, I’m not an academic. But I do think that they’re a good way to communicate and solves some of those problems. And I think Dialyzer actually hits the sweet spot for me of like ‘we’re not gonna interfere with your ability to get stuff done, we’re only gonna tell you when things are truly truly wrong. When we can prove that they’re wrong. And I think that’s basically where I want to be. And people right now are super enamoured with types systems and how types can solve your problems and stuff. And I think that I’m just not sold on that solving the class of problems I’m interested in, just generally. ‘Cause I think that the most interesting problems are not ‘oh, I got the types signature wrong’ or little things like ‘oh, I typed-oed this and my types system told me’. Like, those are not problems that I’m concerned with in any way. And I think, the majority of the time, real problems in real systems have to do with catastrophic failures that you could never have a type system for. Okay, I won’t say never. Someone will come up with a way. And session types are definitely a step in that direction, but generally speaking, the way you actually defend against failure is - you have this cascading effect of multiple services all going down and somebody deleting a file at just the wrong time, and next thing you know, your whole system goes offline. And types don’t necessarily help you with that. And those are more the problems I’m interested in solving. But as far as adding Dialyzer after the fact, I think you could just ignore stuff for the most part. Just like. Do it and run it in CI and have a separate CI bill that will tell you if you’re getting closer. Maybe. If you’re getting fewer errors, but don’t have it block your build, ‘cause otherwise you’re going to be in the camp of ‘now we have to fix all of it and probably some of our dependencies' ‘cause you can’t guarantee that the dependencies that you pulled in ran Dialyzer on their projects. And even if they did, sometimes Dialyzer doesn’t find problems until you actually use that dependency. AMOS: I think it would be nice if somehow the output could say ‘so much of this is right and so much of this is broken’. Or number of errors. I don’t think it does, from what I remember. I don’t know. I didn’t really concentrate on if there was a number. I just concentrated on ‘what does this output mean?’. Which is - that’s its own bag of worms. And then you could also set your build up to say ‘it’s gotta be better than this amount until you get there, then you up that until you’re out. ‘Cause, ya know, this project has been around a while. If we get a thousand lines of output, I’m not gonna know if I add one more. CHRIS: Right. I think generally in engineering, especially with a team of a sufficient size - pick your number. Maybe it’s a size of one - there’s always these things that we do that we consider to be best practices to build good software or something like that. And by and large, these are things that we add to our software after the fact. So something like running Dialyzer. So right now, if you were to run Dialyzer, it’d just be you running it on your local branches, and there’s no way that you can force anybody else and my anecdotal evidence is that entropy will win out and that by and large, people won’t do that stuff because best practices are like billboards. Or warning labels for things. Everybody looks at a warning label and is like ‘well, that’s not for me, that’s for the stupid people. I’m smart. I know more than that warning label and I’m not gonna get hurt’. And that’s when people get hurt. And I think that’s true for best practices. Everybody knows them - even if you become a quote on quote software craftsman or whatever it is people are into these days - you learn all these best practices, and of course, once you’ve learned them all, it gives you this confidence to say ‘I know more than that thing. I don’t need to do that right now. It’s not important.’ I think whenever you do these sorts of things, you can’t just enforce them by convention. You have to enforce them by some sort of rule. And I don’t know how you would go about doing that correctly. AMOS: And you have to automate that rule. ‘Cause I’ve found that even after they all agree on it, there’ll be a large percentage that don’t follow that rule. I’ve seen this in multiple teams that I’ve worked with - PRs. The team will say ‘you need to have x number of approvers on PR’ and then people put the PR up and either they don’t get up to x number of approvers or they don’t get any… They like. Put the PR out there and then merge it. And it’s like ‘oh, you can look at the PR, but I needed it for my next step, so I merged it’. CHRIS: Right. Or the one I hear all the time which is hilariously ironic to me is ‘oh, we really needed to get this done. It was an emergency’. And that’s clearly the time to throw out all your best practices and rubric when there’s an emergency. It’s clearly the time to stop thinking and stop going through the processes. That’s what doctors do after all. If there’s an emergency in the operating room, they’re immediately like ‘oh no, something’s gone wrong. Lets go off the cuff’. AMOS: ‘I’m not washing my hands. There’s a guy dying in there. I just work.’ Now that is a grand analogy. It shows the safety of the issue. CHRIS: I think I stole that from someone, but I don’t remember who I stole it from. AMOS: Well, that’s good. Whoever.... CHRIS: Whoever that was, if you don’t come forward, I’m gonna take credit for it. AMOS: And then you should follow that up with ‘I work better under pressure. It’s okay’. That’s one of my favorites too. ‘Why are you putting that off?’ ‘I work better under pressure’... So, I’ve dabbled in Haskell and I just found out that I really don’t like the way that Java does typing. I don’t know. For some people, that may be the best thing for them. But it just made me angry all the time. So in playing in Haskell, Haskell’s types system is obviously way more powerful than Dialyxer in what you can even specify. So. What are some of the differences that you see in - not the inference part, but like when you’re declaring what is possible to declare and what you can’t in Dialyzer, for people who are coming over to Elixir that have done some Haskell. CHRIS: Right. So. One thing that I wanted to key on that you said that is an important term is ‘powerful’. And I think that is accurate if we’re talking - in as much as we’re - if we’re talking about power from the point of view of computation, and not sort of… more of an - it’s a little bit of the difference of simple versus easy. Like the idea that simple just means like one thing, versus easy which is like ‘close at hand’. I think you’re right in the sense that Haskell in an actual computational sense, that’s not debatable. But there’s a difference between powerful and useful and when we think about these things - I wanted to bring that up. AMOS: What I was thinking when I said powerful was that - now I gotta figure out a way to explain this - it allows you to put tighter constraints on the types, and… Yeah… maybe that’s as far as I can get in my mind. CHRIS: I think that’s accurate. So, Haskell’s type system - and in fact, the majority of type systems. Type algebras that are used in languages like Haskell - so that’s like the entire ML family of languages. It’s Java. Its C sharp. They’re all based on an actual algebra, and the algebra is Hindley Milner type systems. By and large. That’s where the root of a lot of this stuff comes from. The key point in a lot of those type systems is that you start with a basic logical premise - you start with ‘none’ or ‘false’ or whatever. You start with ‘this cannot type check’ and then what you do is - I’m like. Hand-waving over this definition by the way. This is not rigorous. But you start with none, and you look through the code and you build up the type systems, and as long as you don’t end up with none, at the end, everything type checked. So like you layer on all these things and you layer on types on top of none. As long as you don’t get none, everything type checks out and everything works great. Dialyzer and success types generally do the opposite. They start from ‘any’ and they’re permissive. And they traverse backwards to find types that don’t work out. Then we throw an error. And if we can’t do that, well, it’s any, so it works out. I think that’s where Dialyzer is the most useful, because it will only warn you if it can prove something is wrong. And everything else, it’s permissive about. AMOS: The other way - I forget what you call it, but it’s where you start with none. So since when you start with any and you put things together, it only tells you if something is for sure wrong. So if Dialyzer comes back and tells you something isn’t right, something really isn’t right. But if another type system tells you that, then the other type system from starting at ‘none’... Does that mean it might be wrong? I mean, for sure if you fix it, it’s gonna be okay. But. It doesn’t necessarily mean that is wrong. CHRIS: This gets into the history of why Dialyzer was created and how it was created and when it was created… AMOS: Thank you Costas. CHRIS: Thank you Costas… But there were multiple attempts to add a type system - a type algebra - to Erlang. And sorta the - I’m forgetting the original history here - but - I don’t think it was the first attempt, but the first major attempt - was actually done by Waddler, who was influential in Haskell and a bunch of different things. So he tried to write a type algebra for Erlang, I think with some other folks - and they got a decent way into it, and - Erlang was already around and it was already being used in industry. That’s probably why they were trying to write a type algebra for it, ‘cause it was just enough of a functional language. I mean, it’s such a weird mongrel of a language just generally. But it was just enough of a functional language that functional programming academics could latch onto it and do research for it and just be like ‘look we have a functional language in industry being used’. So Waddler attempted to add a type system around it, and they got a decent way in, and it turns out it was really hard to write types around all of the different concurrency stuff, and all the different side effects… coupled with the fact that you would effectively have to force everybody who was writing Erlang to change how they were writing Erlang. You basically would have to say ‘you have to adopt this brand new way of writing Erlang code’. And for people in industry it’s like ‘well, we’re not gonna do that. We’re not gonna change the way that we write this code, ‘cause we know this code works. It’s running right now in production and its working. You can’t come in here and tell me that like. Just ‘cause your type algebra says that it’s not working, I have empirical proof that says it is. And it’s working right there, and I’m not gonna change the way that I write Erlang code just to suit your type algebra.’ That was sort of the context of all of that. And that’s where Costas came in and said ‘we need to approach the problem from the point of view of adoption’. And the way we get people to adopt it is to tell people that they don’t actually have to change the way that they write code. And one of the ways that they did that is that they started from the assumption that the code people had written was correct. So they only needed to inform people when stuff was wrong. I think that’s a very good mindset, just generally. Flow and Typeset both took those approaches to how they could be adopted. They did the whole gradual thing. And that’s a furtherance of the success typing stuff. AMOS: Is Elm the opposite side of that? Is Elm more like Haskell? CHRIS: Yes. AMOS: Okay. I’ve kinda looked into that, but I haven’t touched it very much. CHRIS: I’ve used Flow before. It’s a pretty good type algebra. I know a bunch of people who are really into Typescript. Typescript it seems like probably has longer legs. AMOS: I hear typescript everywhere. CHRIS: Yeah, I hear it a lot. And I mean. That’s the thing. If you want your type algebra to get adopted, it has to come alongside the existing ecosystem and start to just sort of gradually improve the situation. And that’s the same sort of thing. And I just find those things more useful. Generally. It’s not true across the board, but you know. Types help you program in the small and they don’t help you program in the large. And I am just more concerned typically about programming in the large than I am about programing in the small. AMOS: ‘Ultimately, I need to get the bigger problems solved.’ - Is that where you’re coming from? CHRIS: Just that there’s different ways of thinking through like. How does this affect the business? How does this feature affect the system? Am I delivering the feature correctly? And can we prove that this is correct from the point of view of system thinking? Can we prove that this isn’t going to have bottlenecks or like isn’t gonna cause a cascading failure. Or that kind of thing. AMOS: So with that in mind, I’ve seen a lot over the years more on the Haskell side - but I hear people talking about it in the Elixir community - kinda like back room talk - not standing up at the conference talk - so I guess ‘outlaw talk’ - about denotational design. And so writing spec first, and I’ve done it in the small and found it beneficial in getting me to think through what was actually going on, and I think caused me - I don’t know if it saved me time. It probably saved me some bugs, ‘cause of the amount of thinking I did before typing - but - what are your feelings - well, maybe we should define denotational design. So… denotational design is where you design your system up front for specs - all of the specs for whatever feature you’re doing before you ever actually write any function. So you say ‘this type goes into this function and this is the return’. And then you say ‘well okay, now I have this’ - Let’s say you’re doing a bank. You might have a deposit that takes an account and an amount and returns a new account or something. And you set up your system looking at types first. I guess more of a functional, mathematical type design. You’re looking at the flow of the data and how that data is going to change over time. And with that - this is where I was getting earlier with that powerful Haskell-type system. Is that a lot of times, there are extra things that you can put into your types signature that gives a little more information about what’s going on than you can in Elixir. But - using Dialyzer - is it feasible? Or is it just the fact that you’re thinking a little bit more? Have you ever done it? Maybe we’ll start there. CHRIS: I don’t think I’ve ever done that in any sort of a formal sense, where I could have told you that that was what I was doing when I was doing it. I think there’s only good that can come from more systems thinking in terms of inputs, outputs, what are the boundaries of this thing that I’m building. Whether or not that thing is a collaboration of fifty things inside of it - you know, at some point, there’s input coming in and output going out. And where do those things get transformed. I think there’s only good that will come from that sort of design process. But as far as the types go - I don’t know. My feelings on it are very complicated. So much of it has to do with your team - where your team is at, what your team is ready for. Do you need those sort of guiding hands around your team? From a purely ideological standpoint - I mean, this is a great question. I think about things moving through the system in terms of values. What I mean by that is you have pieces of data. And those pieces of data move through the system and you have pieces that come from somewhere and it has some sort of provenance and it has some sort of attribute attached to it. But what makes the data is the data itself. The actual value of the data is what makes the data itself. And I think what makes building systems from a practical sense is you only design the system up front once before you get to build. And most projects are not green field. Most projects spend more time in production and refactoring than they do as a greenfield process with no code. Not to say that the design process is bad, but I tend to think of ways that we can allow for expansion and allow for change, and I think types by their very definition limit expansion and change. And they don’t convery anything about the values. For instance, let’s say I have a user. And I have a user ADT or a user data constructor in Haskell or something like that. And it’s user, and we’re gonna store their age or something like that. And we’re gonna store their email and their twitter handle. And so, its user.int, string, string, string. Well, that doesn't tell you anything about the user. That doesn’t tell you anything useful - it doesn’t convey anything important about what it means to be a user. The value - the actual piece of data - is what conveys the value of a user. I just tend to think about ways of structuring systems where you can accommodate for that kind of growth, for that sort of change, to accommodate new features and that sort of thing… Because of that, I’m more inclined… I don’t think about that in terms of types in the traditional sense because of that. AMOS: Yeah. It’s a little bit to digest. It makes sense to me. They cary some information, but they don’t necessarily carry the important information that you need. And you could do like, type aliases and stuff, so you can make one of those things a username, but it’s still a string. And that’s where, when you say something is a username and then alias it in Haskell, does it then check later if its aliased or does it no longer care? CHRIS: Well, I mean, so part of it is that - I mean, as long as your passing strings in there for emails, then its fine. As long as it passes. You could even do a type alias that says ‘email is string’ and you save a string, that’s fine. It all type checks and that’s great. It doesn't convey anything about what that piece of data is to be an email. It doesn’t tell you anything about what it means to be an email. By and large, that’s all gonna come from the run time. That’s run time stuff. At which point your types don’t matter anymore AMOS: If you have to check the format of an email… CHRIS: Yeah… Which you actually care about is the run time business logic of the stuff. Its such a subtle difference in the way of looking at the programs… I mean, to me, as cool as Haskell and as cool as Rust is… I still use those languages - I use Haskell as my prototype language. If I’ve got really hard problems, Haskell is the language I use to prototype it. Because I can express my problem in simpler stuff. But from the point of view of a running a system in production, I just lean towards the idea of what you do not want are things that limit your ability to defend and grow your system. From a purely ideological standpoint. I mean, all this goes out the window when you confront the fact that you have a team of human beings, all of which have varying skill levels, none of which work in the same room… and its like ‘how do you make all of those things work?’ and types can help you. And I get it. That’s a small problem. To avoid those sort of basic mistakes. Amos: Right. When you have a new person come in and they don’t have to learn the type of the small area. On a bigger system with no types, they might have to go up a few levels before they can actually figure out what’s going on. Or what data they’re getting. Maybe they don’t need to know what’s going on. They just need to know the type of data. CHRIS: And maybe the problems the programmers are solving on your team right now - maybe the problems that you’re facing are programming in the small. I keep using the terms ‘small’ and ‘big’, those aren’t meant to deride those kinds of problems, it’s just to kind of meant to define those types of problems.And maybe that’s exactly what your team needs at that moment. Do whatever it takes to help build better software. AMOS: That’s really the key. And build yourself better too, so tomorrow you can build better software than today you. That’s one of my big things. If I go through the day and I don’t learn something, I feel like I’m in the wrong job. I should look at code I wrote a week ago and think ‘who the heck wrote that’. That’s my goal in life. CHRIS: You had another point which I think was interesting and I wanted to get you opinion on it, which was you were saying that one of the things that inhibited your ability to get stuff done was this lack of integration level testing. By that, I assume that you do have unit level testing, just because of the way you phrased that. Or, you have some testing, but you don’t have this full system collaboration stuff, and I’m kind of curious to get your thoughts on it. ‘Cause I have anecdotal experience and I have my own thoughts on the testing period and the amount of tests you should be writing and how you write them. I want to know what you have to say on that. AMOS: I find it really hard to write integration tests on projects that have been around a long time. So that’s maybe a different topic. I really like integration tests, mainly for happy path, and maybe for like, one failure path. So let’s say I’m gonna have an integration for this test, and somebody has to register an email address. I might test an email address with an @ in it and somebody tries to put one in without an @. That’d be probably the only error case, even though there are lots more ways to mess up an email address, I’ll probably only test one error path and one happy path. And then mainly use unit tests from there on out. I guess the big reason that I’ve run into that is because I’ve seen many integration tests that have done very little on the long run, and take a lot of time to run, so I’m looking at the investment. Is this worth having my test suite run for two days to find a bug that if I’d just done the happy path it might find or if I’d just done one failure path it might find. But I don’t try to do exhaustive integration tests. Now, I have thought about using maybe Wallaby with something like Proper to do stateful testing from the top down, but again, on a system that already exists, just getting it set up could be weeks worth of work, depending on how that system is set up. And how quickly that system is changing at the time. That’s the other thing that I run into with integration testing - if you’re quickly prototyping on the system, and maybe it’s changing frequently or maybe an area of the system is changing a lot, then I really try to minimize how many I’m putting in there, ‘cause they just get super frustrating to change constantly. But I also try to write my integration tests at a level that maybe the steps inside the integration tests I have to change, but the test itself, the wording whenever you’re reading it is not as verbose. So, I’ll create a method whenever you’re reading it that says ‘log in’ and let’s say you’re using Wallaby because it’s a web app. That log in step will do the fill in username, fill in password, and later we say ‘we’re gonna use oath, so we don’t need that username or password. Well, login - that function probably doesn’t need to change. It’s just the underlying part of that step. And the reason why is if you don’t, people start putting ‘username and password’ in every single file and those steps to fill it out and it’s very painful… So I think…. I like testing at all levels. Integration testing less. And this is really long winded, but ultimately, I think that your testing code needs attention just like your regular code. You need to refactor it, you need to look for duplication, but I think you can’t be too rigorous in it, just like in a regular app, because if you do too much refactoring, a lot of times you can get to a place where it’s really hard to follow and really hard to see what’s going on, and especially in testing, I think it’s really important to be able to see ‘this is how I reproduce it from the front end by myself.’ So. Refactor your tests. That’s what I’m saying. CHRIS: I completely agree with that. I saw a lot more test suites, especially when I was consulting, that - yeah. Were in varying levels of needing to be refactored. I think people treat tests… They don’t treat them like real code. They don’t think that they can write real code for them. People don’t do the same sort of decoupling people do when left to their own. They don’t apply those same practices to writing tests which is really kind of a fascinating mental thing. I have a… leading question because I have followup questions, but I’m really curious to see what you think about this - what would you say the point of testing is - in the sense of… some people test because it provides design. And some people write tests because it ensures some level of correctness for some level of correct. Some people do it to like, protect from regressions and whatnot. Why do you write tests? AMOS: Yes. Yeah, all of it. CHRIS: If you had to weight them. Like, most important to least important. AMOS: Oh, that’s really hard… I like… CHRIS: Okay, you can assign a value between one and three to each different aspect of testing, and they can share them. AMOS: Can you go back through the aspects? You name an aspect and I’ll give you a number and maybe a reason why. CHRIS: Design. AMOS: One being the lowest, three being the best? CHRIS: Sure. AMOS: 2.75? CHRIS: I didn’t realise we were going to use floating point, but that’s fine. I hope we don’t get rounding errors. AMOS: Two is… Well, I think that, especially if you’re doing TDD type test code, it really enforce decoupling, because you have to have it decoupled for your test code to work. I think writing tests after the fact is very difficult. And that I’m usually happier with the code if I write the tests first. That said, I sometimes fail at it, especially if I’m on a project that has very few tests and is already tightly coupled, it gets very hard to put these things in there, but I do it where I can. I think that’s an important aspect, but not the only reason. Like I said, all of them are the reason. CHRIS: Regression. Like, in order to stop regressions from happening. AMOS: I don’t want to say it’s not important, but I borderline want to say a one. CHRIS: Hey, man, you can be honest here. This is fine. I’m here to listen to whatever you have to say. I haven’t even told you what I think yet. AMOS: We’re gonna go through this for you, too. CHRIS: No, no. I’m good. Regressions. You said one. AMOS: The reason why is not that I don’t think that they’re important - I would like to say a two or three - but there are so many bad tests out there that aren’t really doing you any good and don’t really catch regressions outside of a tiny window. I think property testing helps that, if you’re gonna do property testing, but property testing, I have a tendency to do after. Do a few other tests, example tests, kinda see where I’m going and then do it. Now, I’ve done property testing first, too, which worked out really well, but it’s hard to do property testing first because you get these weird failures and it doesn’t fail the same way, and it can be pretty rough. Property testing is better for regressions than example testing. CHRIS: I would agree with that. To me, the reason you actually property test is if you want to actually harden a piece of code and ensure some semblance of correctness for code that needs it to be correct, for code that is actually important enough to validate its correctness. Property testing is the right way to do that. It’s a lot closer to something like a formal method. It’s like the non-academic way of trying to do formal methods. It’s like, ‘alright, we’re going to prove that this works through some kind of stochastic, random generation of values, and that’s going to be our mathematical proof of this. AMOS: So I’m going to say one sub three for regression. Three is the level of importance that I think it should have and one is what I see it being useful for out in the real world. CHRIS: That to me is what I see being the most interesting piece of this is that whenever you write tests, you’re doing two very obvious things - two very obvious costs to this. Number one, you’re asking the computer to give up - or, to do something computationally for you for some amount of time. There’s some amount of tolerance for which we’re willing to let things spin and come up with values or whatever. You know, if you’re writing a database or you’re building a database and you’ve made a guarantee that you’re never going to lose anyone’s data, maybe running a property test model on it for a week - literally a week - is a good investment of time. Like, I think Basho did that. They would let stuff soak for four days. AMOS: If you’re making a car that drives by itself. CHRIS: Yeah, go ahead. Maybe spend that time. AMOS: I’m trying to up the stakes a little bit. Killing people seems to do that. CHRIS: Yeah, you can either do that, or you can mine bitcoin. Either way, I feel like you have to choose how you feel about all that, but you’re incurring some cost there, and depending on how you structure your deployment, depending on how you structure your releases, depending on how you structure your team, that cost - everyone is going to have different tolerances for that cost. If you’re building yet another web app that’s just a better UI around what would otherwise be Excel spreadsheet, maybe you don’t need to let that thing spin for another couple of days. Maybe you can just windmill slam that into production with five minutes test spin. But there is a cost. It’s a real thing. And I think the amount you’re willing to pay is directly proportional to the cost of failure, and everyone is going to have different tolerances for that. I bring this up only because people seem to think about the test runs and be like ‘oh, that’s going to take so long’, and it’s like ‘well, I don’t really care. Was it correct?’ I mean, I will never sacrifice test correctness on the altar of test speed. And there’s a lot of people who I feel like are very willing to do that, which is very interesting to me. And this is why this gets into the discussion of ‘why do you test’. And by and large, I think that if you’re testing for correctness, it’s okay if your test runs a little bit long. And this is just very interesting to me. The other aspect of this is the design aspect. AMOS: I thought that was our first one. CHRIS: Well, it was our first one, but this is the other cost to me is that - you made the point, and I think it’s a correct point is that if you write your tests first, and your lazy, you’ll naturally force yourself to decouple things, ‘cause you’ll be like ‘I’m creating ten different models in the database to test this one function. That’s kind of a lot, what’s going on here’. And then you’ll naturally listen to that laziness and good design will fall out of that. I think anecdotally, that’s been true for me. I’ve seen it be true for a lot of people. I don’t know that that’s a hard and fast rule that that would be hard for everyone. AMOS: Oh, oh no. It’s not. I just saw the other day, someone else’s test suite. They had their pull request deined, first of all, because they had fourteen lines of setup that was exactly the same in every single function, and they pulled it out to an internal function to that test file, and everyone complained at them. But the point is that they didn’t feel that pain because they just copied and pasted it. Which is lazy, right. Like we wanna be. To a point. CHRIS: Yeah, maybe applied incorrectly. AMOS: Yeah, it was ultimate laziness. It’s cause they didn’t have to change that setup yet. Then they’ll be angry. CHRIS: Part of the cost to me - it seems to me like every best practice we have, just about, and all these best practices around tests - we literally have this entire philosophy, this entire manifesto, around TDD - the entire goal if it seems to be to force the coupling to happen, to force simpler testing to happen. But the main thing to keep in mind is that every test you write is a piece of testing to your code. Every test you write is one more reason that you won’t be able to extend your system, that extending your system becomes hard, takes more time, more energy, all these kinds of things, because now you have all of these tests that directly couple to your implementation. And don’t you give me that crap about ‘oh, well if you only ever test the external interface and never test the internal stuff, then the external interface never changes and your tests stay the same. Anyone who tells you that doesn’t write tests for production code. That’s not a thing. AMOS: Or they’re way smarter than I am. CHRIS: Or they’re academics. Or code craftsman who are more concerned about how they type the keys than the letters they’re actually typing. And I just don’t buy that. That might be a real thing, but I haven’t seen it to be true. The important bit there is that you are creating coupling. You are doing that. And that’s fine. There’s a cost there that you might be willing to pay. And in some cases, it’s arguable that you do want to create that coupling because it helps to enforce your contracts, you know, if you don’t have types, or if you don’t have this ability like we do in Elixir which is to let things crash or to have guard clauses, or you don’t have something to save yourself from bad data, then you might need those tests, might need that sort of contract in place to help you enforce the type of data you’re willing to accept and process. And that is where I think property testing is very useful, because you can specify a whole range of very interesting values and make sure that your stuff still works for it. Now, that said, it is important to keep in mind that there is a cost associated there. And the question that I would have for you is that if you’re trying to get out of testing is design, and to some degree, you think regression is important to some degree, but not really, why don’t you delete your tests when you’re done writing them? AMOS: Oh, no, I think it is, I just think the regression usefulness of most tests that I see is a one. CHRIS: Oh, I see. Is there any chance that this is a local minimum, not necessarily a global minimum? AMOS: I’m gonna go back to the TDD thing. That regression stuff? I actually see it as less useful when people are testing after they wrote it. It seems like there’s often less tests, or the tests are more brittle ‘cause they’re just testing the code that they wrote. They just look at their code and say ‘okay, how can I make this pass?’ And that’s where I think the regression ends up as a one. That’s why I said one sub three earlier. ‘Cause in the real world that I’ve seen on most projects - not all - was that regression was borderline a one. Ideally, it’s a three and the reason I never saw it as being… well, you should never see it. If it’s working for regression, you should never see it, because it should be fixed on the developer’s screen before it gets to a pull request. If it’s actually working for regression. Or on the build system. It should never get put into Master, because the build should fail. CHRIS: Right, well, in a theoretical sense, right. If you’re talking about purely regressions. But you know the one commonality between all bugs found in production? They pass the type checker and they pass the test suite. So, I don’t know. I tend to think that as much as I like using tests or using certain things like that to help inform design, I’m not sold on that as a universal. I don’t think it’s a hard and fast rule, I think it’s a useful tool if that’s what you put in your toolbox. I’m pretty in the camp of ‘I write a lot of stuff and then I either transform it into properties if I care about it being correct or I delete the test because its not useful to me anymore after its done as a design too. ‘Cause it’s like ‘now it’s getting in the way of me being able to grow and extend my system and I don’t need to do that.’ AMOS: I don’t know if we can be friends. CHRIS: That’s fine. AMOS: I’m pretty hard and fast in the opposite camp. I mean, I’m pretty into like having tests and TDD pretty heavily. CHRIS: I’m not saying it’s a bad thing. AMOS: But I don’t want to delete them. CHRIS: That’s fine. You don’t have to delete them. I’m just saying for me - for me and the kind of work that I’m doing - and this is getting back to the types thing. The problems that I am concerned with problems are such that they’re not the reason I write tests like that. To me, the most useful things are the things that stop bugs and that means its typically integretion tests ‘cause the integretions tests are actually testing things in a real world system, and that’s the kinds of problems that I’m solving, by and large, like. Those are the kinds of things that I am focusing on. And I think its important - whenever these debates happen, there’s a lack of the context that’s added to the conversation thats ‘what is the actual problem you’re trying to solve or what is the actually goal that you’re trying to achieve? Are you building a webapp or are you building a database or are you building whatever? And I feel like there’s a lack of nuance that’s talked about because people don’t talk about the real world costs of it and they don’t talk about what they’re willing to pay. No one has a budget for this stuff. There’s plenty of people who will tell you how to do TDD and how to do TDD well… But there’s no financial advisor for this kind of stuff and no one talks about what kind of techniques they have and where they’re most applicable and that kind of stuff. AMOS: That’s a hard problem and it has a lot of I think opinion - do some science and figure it out pretty quickly with some experiments or something it’d be done. CHRIS: Yeah. It turns out running the - the only papers we have are from industry, which are already biased or its some researcher testing this on his grad students and it’s like all five grad students. AMOS: Yeah, I was talking to a researcher a few years ago and he was saying that that’s the hardest thing about researching computer science. He’s researching languages and what can make people write code quickly and effectively, or what can make people comprehend code, so he shows them all different types of code… And he says the problem is that you don’t get to test on seasoned professionals because you can’t afford to pay them to sit there for eight hours and do this, so you have comp sci college students and non-comp sci college students, ‘cause no professional is going to take the time to come in and do this - to be statistically significiant… Well. I’ve got a lot of stuff to get to today. CHRIS: Yeah, we’re running long. And I think we’ve really hit the high points here. AMOS: We did testing, Dialyzer, kid stuff… CHRIS: Yeah, it’s good. And Anna will be back next week to keep us on track, as she does. AMOS: Two new Elixir friends - we’re just gonna keep that a surprise. CHRIS: Yeah. That’s a teaser. That’s some pro shit right there. Alright, man. AMOS: Alright. Take it easy. CHRIS: Bye. AMOS: Bye.