Tumgik
#as always i wrote ts over the span of a few hours so the time aint accurate atp. its now 1:43pm on the same day. just wanted to clarify
Text
I for the first time ever I deleted a post. I'm gonna make that a habit from here on. I'm trying to work on how I communicate when I'm upset annnnd I very clearly was not in the right space at that moment.
Though now that I'm out that daze I don't know how to pick up the pieces. I don't know why I quit but I did atp knowing full well I do not have the money for my uni debt installment.. I nonchalantly took 700 right after rhe fact planning on easing up to much much more in the hopes of escaping that whole situation
I got scared, didn't do the rest, discovered that our landlord was doing an inspection soon, and finally, used that as an excuse and pushed it off til then
And now?
Today is the day of. Well actually ig its the day after now. It's 1:50 am and Saturday atp lol. Dooms day was technically supposed to be on Fri
I've been feeling so trapped. I genuinely don't know what to do from here. Its so fucking stupid. If I could have pushed through for just a week or two longer half of this issue wouldn't even matter. But no. I got so frustrated I just quit on the spot and I was already being stupid before too. I just can't get over this fucking hurdle. I can't. It's like anything I don't want to do just turns into the bigger than it ever needs to be and it takes my all to just get through it. That shit is so draining
But my god like it's not draining enough to where I need to like whole ass unalive myself.. shit. Everytime I come out that daze I cringe at how overdramatic I sound. It's because of my giant problem with asking for help. It is HORRIBLE. The thought of telling anyone what I've been struggling with just sounds like an emotional mess
But everytime I think on it, it feels more and more like my only option is to hurt myself. I so desperately need psychiatric help but who has the money for that? If I don't hurt myself, it won't be considered urgent enough for them to get me help right away. Plus they'd get annoyed with my constant excuses. Not to say they wouldn't still be mad if I were to do something like that.. tho least they'd know it's different this time.
But on the other is it right for me to purposely weaponize my self inflicted suffering to get help right when I want it? Is it manipulative? Is it a necessary sacrifice? I've been wanting this for so long. If i could just keep my head on straight for long enough maybe I could fucking afford it myself.
That's what I hate about it. It's a two in one fuck up. I have $300 I'm somehow supposed to poof up by the end of the month. Tbh I have like $170 more I need to sort out too but it's not as urgent lol. But thennnn that whole sink hole issue plus me quitting.. AGAIN
I literally don't know what to do. I don't want to do it again. I fucking hate the taste, the feel, the everything. I relapsed out of pure desperation and i still was miserable. Worst two days of my life. I felt so pathetic
So now it makes it sting so much worse for that being all that I can think of. Ik I'd get help. But god.. do I have to feel that shit to get it? Do I really have to? What are my other options tho
Jesus. I am so ready to shut down and hopefully just stop breathing. I'm terrified of how bad I'm gonna hurt after I take them all. But I really don't see any other options. The thought of asking for help makes me sick. They're not gonna take it seriously. Ik they'd cheese along originally but they'll get annoyed and hate me. Plus what am I gonna do in December? I still have debt to pay then. That shit is still gon be due.. that's another $200 I need to 100% have or else im gonna fuck up everything. My mom's cosigned on my student loan I literally can't fuck it up.
I've been depersonalizing, dry heaving, and ofc boohooing about it all for so many days. I just want to shut down and not do anything anymore. I don't want to do anything atp. I just don't want to be stuck throwing up and dehydrated again. It feels so gross.
I want to talk to R about it. Disregarding all the extra shit we've been doing lately. Im putting all that bs aside for now. I know that she went through similar. That is it. I hope she's not too busy
0 notes
Quote
We ported our React frontend from JavaScript to TypeScript, but left the backend in Ruby. Eventually, we ported the backend to TypeScript too. With the Ruby backend, we sometimes forgot that a particular API property held an array of strings, not a single string. Sometimes we changed a piece of the API that was referenced in multiple places but forgot to update one of those places. These are normal dynamic language problems in any system whose tests don't have 100% test coverage. (And it will still happen even with 100% coverage; it's just less likely.) At the same time, these kinds of problems were gone from the frontend since porting it to TypeScript. I had more experience with backend development, but I was making more simple errors in the backend than the frontend. That was a sign that porting the backend was a good idea. I ported the backend from Ruby to TypeScript in about two weeks in March of 2019. It went well! We pushed it to production, which was then in a closed beta, on April 14, 2019. Nothing blew up; users didn't notice. Here's a timeline of the run-up to the backend port, plus the time immediately after: I wrote an unusual amount of custom infrastructure during that port. We have a custom 200-line test runner; a custom 120-line database model library; and a larger API router library that spans the frontend and backend code. Of our custom infrastructure, the router is the most interesting piece to discuss. It wraps Express, enforcing the API types that are shared between the client and server code. That means that when one side of the API changes, the other side won't even compile until it's updated to match. Here's the backend handler for the blog post list, one of the simplest in the system: router.handleGet(api.blog, async () => { return { posts: blog.posts, } }) If we rename the posts key to blogPosts, we get a compile error ending with the line below. (The actual object types are removed from the error message to keep it short here.) Property 'posts' is missing in type '...' but required in type '...'. Each endpoint is defined by an api.someNameHere object, which is shared between the client and server. Notice that the handler definition doesn't name any types directly; they're all inferred from the api.blogIndex argument. This works for trivial endpoints like blog above, but it also works well for complex endpoints. For example, our lesson API endpoint has a deep key .lesson.steps[index].isInteractive, which is a boolean. All of these mistakes are now impossible: If we try to access isinteractive on the client or to return it from the server, that won't compile; it has to be isInteractive, with a capital "I". If the server returns a number for isInteractive, it won't compile. If the client stores isInteractive in a variable of type number, it won't compile. If we change the API definition itself to say that isInteractive is a number rather than a boolean, then neither the client nor server will compile until they're fixed. None of that involves code generation; it's done using io-ts and a couple hundred lines of custom router code. There's overhead in defining these API types, but it's not difficult. When changing the API's structure, we have to know how the structure is changing. We write our understanding down in the API type definitions, then the compiler shows us all of the places that have to be fixed. It's difficult to appreciate how valuable this is until you've used it for a while. We can move large sub-objects in the API from one place to another, rename their keys, split one object into two separate objects, merge multiple objects into one new one, or split and merge entire endpoints, all without worrying about whether we missed a corresponding change in the client or server. Here's a real example. I recently spent around 20 hours redesigning Execute Program's API over four weekends. The entire API structure changed, totaling tens of thousands of lines of diff across the API, server, and client. I redesigned the server-side route definition code (like handleGet shown above); rewrote all of the type definitions for the API, making huge structural changes to many of them; and rewrote every part of the client that called the API. 246 of our 292 source files were modified in this change. Throughout most of the redesign, I relied only on the type system. In the final hour of the 20-hour process, I started running the tests, which mostly passed. At the very end, we did a full manual testing pass and found three small bugs. All three bugs were logic errors: conditionals that accidentally went the wrong way, which type systems usually can't detect. The bugs were fixed in a few minutes. That redesigned API was deployed a few months ago, so it served you this blog post (and everything else on Execute Program). (This doesn't mean that a static type system will guarantee that our code is always correct, or guarantee that we don't need tests. But refactoring becomes much easier. We'll talk about the larger question of testing in the next post.) There is one place where we do code generation: we use schemats to generate type definitions from our database structure. It connects to the Postgres database, looks at the columns' types, and dumps corresponding TypeScript type definitions to a normal ".d.ts" file used by the rest of the application. The database schema type file is regenerated by our migration script on every migration run, so we don't do any manual maintenance on those types. The database models use the database type definitions to ensure that application code accesses every part of the database correctly. No missing tables; no missing columns; no putting a null in a non-nullable column; no forgetting to handle null in columns that are nullable; all statically verified at compile time. All of that together creates an unbroken statically typed chain from the database all the way to the frontend React props: If a database column's type changes, other server-side code (like the API handlers) won't compile until everything is updated to match. If the server-side API handlers don't match the client-side API consumers, one or both won't compile. If the client-side React components don't match the data coming out of the API, they won't compile. Since finishing this port, I don't remember any API mismatch making it past the compiler. We've had no production failures due to mistakes where the two sides of the API disagree about the shape of the data. This isn't due to automated testing; we don't write any tests for the API itself. These guarantees are wonderful: we can focus on the parts of the app that matter! I spend very little time wrangling types – far less than I spent chasing down confusing errors that propagated through layers of Ruby or JavaScript code before causing a confusing exception somewhere far away from the original source of the bug. Here's a timeline of our development since the backend port. We've had a lot of time and new code to evaluate the results:
http://damianfallon.blogspot.com/2020/04/porting-to-typescript-solved-our-api_25.html
0 notes
Quote
We ported our React frontend from JavaScript to TypeScript, but left the backend in Ruby. Eventually, we ported the backend to TypeScript too. With the Ruby backend, we sometimes forgot that a particular API property held an array of strings, not a single string. Sometimes we changed a piece of the API that was referenced in multiple places but forgot to update one of those places. These are normal dynamic language problems in any system whose tests don't have 100% test coverage. (And it will still happen even with 100% coverage; it's just less likely.) At the same time, these kinds of problems were gone from the frontend since porting it to TypeScript. I had more experience with backend development, but I was making more simple errors in the backend than the frontend. That was a sign that porting the backend was a good idea. I ported the backend from Ruby to TypeScript in about two weeks in March of 2019. It went well! We pushed it to production, which was then in a closed beta, on April 14, 2019. Nothing blew up; users didn't notice. Here's a timeline of the run-up to the backend port, plus the time immediately after: I wrote an unusual amount of custom infrastructure during that port. We have a custom 200-line test runner; a custom 120-line database model library; and a larger API router library that spans the frontend and backend code. Of our custom infrastructure, the router is the most interesting piece to discuss. It wraps Express, enforcing the API types that are shared between the client and server code. That means that when one side of the API changes, the other side won't even compile until it's updated to match. Here's the backend handler for the blog post list, one of the simplest in the system: router.handleGet(api.blog, async () => { return { posts: blog.posts, } }) If we rename the posts key to blogPosts, we get a compile error ending with the line below. (The actual object types are removed from the error message to keep it short here.) Property 'posts' is missing in type '...' but required in type '...'. Each endpoint is defined by an api.someNameHere object, which is shared between the client and server. Notice that the handler definition doesn't name any types directly; they're all inferred from the api.blogIndex argument. This works for trivial endpoints like blog above, but it also works well for complex endpoints. For example, our lesson API endpoint has a deep key .lesson.steps[index].isInteractive, which is a boolean. All of these mistakes are now impossible: If we try to access isinteractive on the client or to return it from the server, that won't compile; it has to be isInteractive, with a capital "I". If the server returns a number for isInteractive, it won't compile. If the client stores isInteractive in a variable of type number, it won't compile. If we change the API definition itself to say that isInteractive is a number rather than a boolean, then neither the client nor server will compile until they're fixed. None of that involves code generation; it's done using io-ts and a couple hundred lines of custom router code. There's overhead in defining these API types, but it's not difficult. When changing the API's structure, we have to know how the structure is changing. We write our understanding down in the API type definitions, then the compiler shows us all of the places that have to be fixed. It's difficult to appreciate how valuable this is until you've used it for a while. We can move large sub-objects in the API from one place to another, rename their keys, split one object into two separate objects, merge multiple objects into one new one, or split and merge entire endpoints, all without worrying about whether we missed a corresponding change in the client or server. Here's a real example. I recently spent around 20 hours redesigning Execute Program's API over four weekends. The entire API structure changed, totaling tens of thousands of lines of diff across the API, server, and client. I redesigned the server-side route definition code (like handleGet shown above); rewrote all of the type definitions for the API, making huge structural changes to many of them; and rewrote every part of the client that called the API. 246 of our 292 source files were modified in this change. Throughout most of the redesign, I relied only on the type system. In the final hour of the 20-hour process, I started running the tests, which mostly passed. At the very end, we did a full manual testing pass and found three small bugs. All three bugs were logic errors: conditionals that accidentally went the wrong way, which type systems usually can't detect. The bugs were fixed in a few minutes. That redesigned API was deployed a few months ago, so it served you this blog post (and everything else on Execute Program). (This doesn't mean that a static type system will guarantee that our code is always correct, or guarantee that we don't need tests. But refactoring becomes much easier. We'll talk about the larger question of testing in the next post.) There is one place where we do code generation: we use schemats to generate type definitions from our database structure. It connects to the Postgres database, looks at the columns' types, and dumps corresponding TypeScript type definitions to a normal ".d.ts" file used by the rest of the application. The database schema type file is regenerated by our migration script on every migration run, so we don't do any manual maintenance on those types. The database models use the database type definitions to ensure that application code accesses every part of the database correctly. No missing tables; no missing columns; no putting a null in a non-nullable column; no forgetting to handle null in columns that are nullable; all statically verified at compile time. All of that together creates an unbroken statically typed chain from the database all the way to the frontend React props: If a database column's type changes, other server-side code (like the API handlers) won't compile until everything is updated to match. If the server-side API handlers don't match the client-side API consumers, one or both won't compile. If the client-side React components don't match the data coming out of the API, they won't compile. Since finishing this port, I don't remember any API mismatch making it past the compiler. We've had no production failures due to mistakes where the two sides of the API disagree about the shape of the data. This isn't due to automated testing; we don't write any tests for the API itself. These guarantees are wonderful: we can focus on the parts of the app that matter! I spend very little time wrangling types – far less than I spent chasing down confusing errors that propagated through layers of Ruby or JavaScript code before causing a confusing exception somewhere far away from the original source of the bug. Here's a timeline of our development since the backend port. We've had a lot of time and new code to evaluate the results:
http://damianfallon.blogspot.com/2020/04/porting-to-typescript-solved-our-api_12.html
0 notes
Quote
We ported our React frontend from JavaScript to TypeScript, but left the backend in Ruby. Eventually, we ported the backend to TypeScript too. With the Ruby backend, we sometimes forgot that a particular API property held an array of strings, not a single string. Sometimes we changed a piece of the API that was referenced in multiple places but forgot to update one of those places. These are normal dynamic language problems in any system whose tests don't have 100% test coverage. (And it will still happen even with 100% coverage; it's just less likely.) At the same time, these kinds of problems were gone from the frontend since porting it to TypeScript. I had more experience with backend development, but I was making more simple errors in the backend than the frontend. That was a sign that porting the backend was a good idea. I ported the backend from Ruby to TypeScript in about two weeks in March of 2019. It went well! We pushed it to production, which was then in a closed beta, on April 14, 2019. Nothing blew up; users didn't notice. Here's a timeline of the run-up to the backend port, plus the time immediately after: I wrote an unusual amount of custom infrastructure during that port. We have a custom 200-line test runner; a custom 120-line database model library; and a larger API router library that spans the frontend and backend code. Of our custom infrastructure, the router is the most interesting piece to discuss. It wraps Express, enforcing the API types that are shared between the client and server code. That means that when one side of the API changes, the other side won't even compile until it's updated to match. Here's the backend handler for the blog post list, one of the simplest in the system: router.handleGet(api.blog, async () => { return { posts: blog.posts, } }) If we rename the posts key to blogPosts, we get a compile error ending with the line below. (The actual object types are removed from the error message to keep it short here.) Property 'posts' is missing in type '...' but required in type '...'. Each endpoint is defined by an api.someNameHere object, which is shared between the client and server. Notice that the handler definition doesn't name any types directly; they're all inferred from the api.blogIndex argument. This works for trivial endpoints like blog above, but it also works well for complex endpoints. For example, our lesson API endpoint has a deep key .lesson.steps[index].isInteractive, which is a boolean. All of these mistakes are now impossible: If we try to access isinteractive on the client or to return it from the server, that won't compile; it has to be isInteractive, with a capital "I". If the server returns a number for isInteractive, it won't compile. If the client stores isInteractive in a variable of type number, it won't compile. If we change the API definition itself to say that isInteractive is a number rather than a boolean, then neither the client nor server will compile until they're fixed. None of that involves code generation; it's done using io-ts and a couple hundred lines of custom router code. There's overhead in defining these API types, but it's not difficult. When changing the API's structure, we have to know how the structure is changing. We write our understanding down in the API type definitions, then the compiler shows us all of the places that have to be fixed. It's difficult to appreciate how valuable this is until you've used it for a while. We can move large sub-objects in the API from one place to another, rename their keys, split one object into two separate objects, merge multiple objects into one new one, or split and merge entire endpoints, all without worrying about whether we missed a corresponding change in the client or server. Here's a real example. I recently spent around 20 hours redesigning Execute Program's API over four weekends. The entire API structure changed, totaling tens of thousands of lines of diff across the API, server, and client. I redesigned the server-side route definition code (like handleGet shown above); rewrote all of the type definitions for the API, making huge structural changes to many of them; and rewrote every part of the client that called the API. 246 of our 292 source files were modified in this change. Throughout most of the redesign, I relied only on the type system. In the final hour of the 20-hour process, I started running the tests, which mostly passed. At the very end, we did a full manual testing pass and found three small bugs. All three bugs were logic errors: conditionals that accidentally went the wrong way, which type systems usually can't detect. The bugs were fixed in a few minutes. That redesigned API was deployed a few months ago, so it served you this blog post (and everything else on Execute Program). (This doesn't mean that a static type system will guarantee that our code is always correct, or guarantee that we don't need tests. But refactoring becomes much easier. We'll talk about the larger question of testing in the next post.) There is one place where we do code generation: we use schemats to generate type definitions from our database structure. It connects to the Postgres database, looks at the columns' types, and dumps corresponding TypeScript type definitions to a normal ".d.ts" file used by the rest of the application. The database schema type file is regenerated by our migration script on every migration run, so we don't do any manual maintenance on those types. The database models use the database type definitions to ensure that application code accesses every part of the database correctly. No missing tables; no missing columns; no putting a null in a non-nullable column; no forgetting to handle null in columns that are nullable; all statically verified at compile time. All of that together creates an unbroken statically typed chain from the database all the way to the frontend React props: If a database column's type changes, other server-side code (like the API handlers) won't compile until everything is updated to match. If the server-side API handlers don't match the client-side API consumers, one or both won't compile. If the client-side React components don't match the data coming out of the API, they won't compile. Since finishing this port, I don't remember any API mismatch making it past the compiler. We've had no production failures due to mistakes where the two sides of the API disagree about the shape of the data. This isn't due to automated testing; we don't write any tests for the API itself. These guarantees are wonderful: we can focus on the parts of the app that matter! I spend very little time wrangling types – far less than I spent chasing down confusing errors that propagated through layers of Ruby or JavaScript code before causing a confusing exception somewhere far away from the original source of the bug. Here's a timeline of our development since the backend port. We've had a lot of time and new code to evaluate the results:
http://damianfallon.blogspot.com/2020/04/porting-to-typescript-solved-our-api_9.html
0 notes
Quote
We ported our React frontend from JavaScript to TypeScript, but left the backend in Ruby. Eventually, we ported the backend to TypeScript too. With the Ruby backend, we sometimes forgot that a particular API property held an array of strings, not a single string. Sometimes we changed a piece of the API that was referenced in multiple places but forgot to update one of those places. These are normal dynamic language problems in any system whose tests don't have 100% test coverage. (And it will still happen even with 100% coverage; it's just less likely.) At the same time, these kinds of problems were gone from the frontend since porting it to TypeScript. I had more experience with backend development, but I was making more simple errors in the backend than the frontend. That was a sign that porting the backend was a good idea. I ported the backend from Ruby to TypeScript in about two weeks in March of 2019. It went well! We pushed it to production, which was then in a closed beta, on April 14, 2019. Nothing blew up; users didn't notice. Here's a timeline of the run-up to the backend port, plus the time immediately after: I wrote an unusual amount of custom infrastructure during that port. We have a custom 200-line test runner; a custom 120-line database model library; and a larger API router library that spans the frontend and backend code. Of our custom infrastructure, the router is the most interesting piece to discuss. It wraps Express, enforcing the API types that are shared between the client and server code. That means that when one side of the API changes, the other side won't even compile until it's updated to match. Here's the backend handler for the blog post list, one of the simplest in the system: router.handleGet(api.blog, async () => { return { posts: blog.posts, } }) If we rename the posts key to blogPosts, we get a compile error ending with the line below. (The actual object types are removed from the error message to keep it short here.) Property 'posts' is missing in type '...' but required in type '...'. Each endpoint is defined by an api.someNameHere object, which is shared between the client and server. Notice that the handler definition doesn't name any types directly; they're all inferred from the api.blogIndex argument. This works for trivial endpoints like blog above, but it also works well for complex endpoints. For example, our lesson API endpoint has a deep key .lesson.steps[index].isInteractive, which is a boolean. All of these mistakes are now impossible: If we try to access isinteractive on the client or to return it from the server, that won't compile; it has to be isInteractive, with a capital "I". If the server returns a number for isInteractive, it won't compile. If the client stores isInteractive in a variable of type number, it won't compile. If we change the API definition itself to say that isInteractive is a number rather than a boolean, then neither the client nor server will compile until they're fixed. None of that involves code generation; it's done using io-ts and a couple hundred lines of custom router code. There's overhead in defining these API types, but it's not difficult. When changing the API's structure, we have to know how the structure is changing. We write our understanding down in the API type definitions, then the compiler shows us all of the places that have to be fixed. It's difficult to appreciate how valuable this is until you've used it for a while. We can move large sub-objects in the API from one place to another, rename their keys, split one object into two separate objects, merge multiple objects into one new one, or split and merge entire endpoints, all without worrying about whether we missed a corresponding change in the client or server. Here's a real example. I recently spent around 20 hours redesigning Execute Program's API over four weekends. The entire API structure changed, totaling tens of thousands of lines of diff across the API, server, and client. I redesigned the server-side route definition code (like handleGet shown above); rewrote all of the type definitions for the API, making huge structural changes to many of them; and rewrote every part of the client that called the API. 246 of our 292 source files were modified in this change. Throughout most of the redesign, I relied only on the type system. In the final hour of the 20-hour process, I started running the tests, which mostly passed. At the very end, we did a full manual testing pass and found three small bugs. All three bugs were logic errors: conditionals that accidentally went the wrong way, which type systems usually can't detect. The bugs were fixed in a few minutes. That redesigned API was deployed a few months ago, so it served you this blog post (and everything else on Execute Program). (This doesn't mean that a static type system will guarantee that our code is always correct, or guarantee that we don't need tests. But refactoring becomes much easier. We'll talk about the larger question of testing in the next post.) There is one place where we do code generation: we use schemats to generate type definitions from our database structure. It connects to the Postgres database, looks at the columns' types, and dumps corresponding TypeScript type definitions to a normal ".d.ts" file used by the rest of the application. The database schema type file is regenerated by our migration script on every migration run, so we don't do any manual maintenance on those types. The database models use the database type definitions to ensure that application code accesses every part of the database correctly. No missing tables; no missing columns; no putting a null in a non-nullable column; no forgetting to handle null in columns that are nullable; all statically verified at compile time. All of that together creates an unbroken statically typed chain from the database all the way to the frontend React props: If a database column's type changes, other server-side code (like the API handlers) won't compile until everything is updated to match. If the server-side API handlers don't match the client-side API consumers, one or both won't compile. If the client-side React components don't match the data coming out of the API, they won't compile. Since finishing this port, I don't remember any API mismatch making it past the compiler. We've had no production failures due to mistakes where the two sides of the API disagree about the shape of the data. This isn't due to automated testing; we don't write any tests for the API itself. These guarantees are wonderful: we can focus on the parts of the app that matter! I spend very little time wrangling types – far less than I spent chasing down confusing errors that propagated through layers of Ruby or JavaScript code before causing a confusing exception somewhere far away from the original source of the bug. Here's a timeline of our development since the backend port. We've had a lot of time and new code to evaluate the results:
http://damianfallon.blogspot.com/2020/04/porting-to-typescript-solved-our-api.html
0 notes