A Survey of Surprisingly Difficult Things
CC Attribution - ShareAlike 3.0 Unported:
You are free to use, adapt and copy, distribute and transmit the work or content in adapted or unchanged form for any legal and non-commercial purpose as long as the work is attributed to the author in the manner specified by the author or licensor and the work or content is shared also in adapted form only under the conditions of this
Boster, Alex
Many seemingly simple "real-world" things end up being much more complicated than anticipated, especially if it's a developer's first time dealing with that particular thing. Classic examples include money and currency, time, addresses, human names, and so on. We will survey a number of these common areas and the state of best practices, or lack thereof, for handling them in Rails.
 All right, let's get started. I can't see very well out there, so shout if you feel the need to ask questions or interrupt. How's the conference for everybody so far? Good? All right, excellent. All right, this thing's not going to work apparently. So I'm Alex Boster. I work for Apfolio, which is a company in Santa Barbara. I work in their San Diego engineering office. We're hiring. Come talk to me. So the last I would have said one particular project inspired this talk, but really it's also the culmination of the kind of experience you get after a few years of doing web development. This is a survey of surprisingly difficult things. What things do I mean? Commonplace things that you know all about in your day-to-day life, so they're easy to model and you model them and they're great, and then it turns out it's actually a lot harder than that. Things where the obvious implementation may very well cause problems. And in fact, you know, these are things like timestamps, time zones, physical addresses, human names. Now when you hear these terms, what do you think? Does this sound easy? These are solved problems, right? Easy. No problem. No complications. What am I not going to talk about? I'm not going to talk about cache invalidation or distributed systems or other also surprisingly difficult things, but this is about real-world stuff. So one of the things, you know, one of the reasons I wanted to give this talk was that developers fall into these traps all the time. We spent months cleaning up old buggy code and then new bugs of the same type are introduced six months later by other developers. So maybe, you know, with a laundry list of things to be aware to watch out for, maybe you won't fall into that trap. And even very senior developers, you know, it might help to just have the occasional reminder here. If you're not a senior developer and you haven't dealt with this stuff much before, then hopefully this talk will save you some time in the future. Another good thing is that if you follow these best practices or similar things, then your app can be more inclusive. So I know that when I start dealing with these world-world things, you know, it causes me to want to drink. Might just drive you to drink, too. So let's start with time. So there are a bunch of time and date classes available to you. The only one I really want to draw attention to, this is mostly for reference later, but the one I want to draw attention to is the last line where there's just no good cross system standard for duration. It's different in every database. It's different in Ruby than it is in the databases. So pay a little attention to that and check out active support duration. But what makes time actually hard to deal with is not this. It's time zones. So again, isn't this a solved problem? You can just have a sufficiently large integer to represent seconds or fractions of a second. And now you have a time value and you're good, right? No problems. Let's all go home. Well, again, the problem is in time zones. So how many time zones do you think there are? Anyone? Thirty something? Say it again? Forty? Well, you may be right, you know, at a certain level. Actually the time zone database, which I'll talk about in a minute, defines 385 time zones and then has a further 176 internal links, which are aliases basically to give them different names. So something to remember, there are half hour time zones. There are quarter hour time zones. You've got daylight savings time to take into account. Daylight savings time may start, a place may start observing daylight savings time that previously didn't, like Arizona currently doesn't. A place may change their schedule like the entire United States did 10 years ago or so and shifted when daylight savings time started. I don't know if this is true currently, but certainly in the past, we've seen examples of two hour daylight savings time changes. This was called double summertime in the UK. And a place may change time zones entirely. They may just switch. So this time zone database I spoke of is used in many Unix-like systems. It's used all over the place with a small band of dedicated developers. It tracks all geographic time zones since 1970 and they define an area as, you know, where any two places share the same time zone at the same time at the same time. So not before, seriously, before 1970, they don't care as much, but they do have historical data. And it does track daylight savings time changes. So an example of this is updated several times a year. If you think this stuff is static, no. It's updated several times a year and here's an example of some release notes. You can read through that quickly. Mongolia no longer observes daylight savings time. This region moved from one to another year round and the clocks starting at this particular time hived off a new area which also affects part of Antarctica. This change fixed many entries for historical time for Madrid before 1979. And it noted that Ecuador switched, actually observed daylight savings time in a particular bit. The exact details aren't important, but just know this stuff is really complicated and they go to somebody's keeping track of it. And that's an example of all the actual regions defined. That's, again, just unique regions since 1970. Another little trivia, you know, how many time zones are in the United States according to this map? Just the continental United States. I count at least six. So we use UTC as a way to standardize things, you know, when things happen at the same instant in time regardless of what you actually call that time in a particular place. Hopefully we know that that stands for neither coordinated universal time nor Thomson or Dursel coordinate, named by diplomats no doubt. UTC is not a time zone, but every time zone has an offset from UTC. And as a rule, you should store your time values in UTC. So also before I proceed, how many, what are the possible offsets from UTC? UTC is kind of in the middle and you can go forward from it and you can go back. How far do you, how do they go? Anyone want to take a guess? That is exactly what I thought. So in 1995, Kiribati got tired of having their country in two different days. So they moved the time zone of some of their outlying islands from minus 10 to plus 14. So there are plus 14 time zones on that side. So keep in mind, without a time zone, any time value you have without context could take place within a 26-hour range, possible half-hour or quarter-hour increments. So how do you handle this? Well, if you don't explicitly provide a time zone, a time you provide could be interpreted using the operating systems default, using your databases default, or using your applications default time zone. That should be configured in your Rails app. Okay, now I need a slightly bigger drink. So as we said, keep your system and database time in UTC, Rails will store its date times in UTC, and time zone-aware methods in Rails will use the applications default if you don't overwrite it with my expressly providing one. So for example, if you have users, be sure to store a time zone on the user's model and always use that in your views if you care at all about when things occur for said users. And just as an example here, you can see that you want to use time.zone.now. I'll talk about this a little more in a minute. And ActiveSupport provides some really sophisticated stuff around it. So don't just use bare Ruby time, use the Rails classes for all this stuff. So these time zone-aware methods, you can see we're parsing two different times in different time zones, but they're actually the same time. So it all works. That's cool. And here are some examples of methods you should use. Hours from now, days ago, those are all good. Always do time.zone.parse, don't do time.parse. If you use string parse time in the middle here, always use in time zone at the end or you will be screwed. Remember definitely time.current to other methods for getting the current time. And the UTC ISO 8601 is for if you're providing an API. Excuse me, if you're providing something to an API. These examples are all from a stolen shamelessly from a blog post that I note there on the bottom. So dates are simpler, right? Dates don't have a time zone. How do you know if you should be storing something in a date or a time? Ask yourself, does it matter what time of day? This seems really basic, but people make this mistake all the time and just convert a date to a time willy-nilly. So don't do this. So what are some examples of dates? Well, birthdays. A day occurs on a day. We don't generally observe when the actual minute of the day a person was born. All day calendar events, maybe holidays you might think of. So for example, I don't know, you know, to take a Western example, Christmas is on the 25th regardless of whether or not you're in Beijing or Toronto or wherever. So as I said, don't store dates and date times. You will have problems. Be very leery of converting back and forth. You almost never want to do that. For example, if you, the one case I can think of offhand is if you have a calendar that you've written and you want to convert, if somebody's editing an event and it goes from being, say, an all day event to a timed event, then maybe. So let's see, where are we? So this is fine. Where did that come from? I don't know. So you want to use date.current because I ran these two lines in the middle of the day, seconds from each other, and that's what I got. What happened? Why did it behave that way? Anyone? Sorry? Yeah, basically. So I'm going to use date.current. The other one is basically telling me that in London it's the 24th. But I see date.today all over the place in code. So these are really the only two date methods you should be using. And you absolutely have to avoid time.now, time.parse, time.string.parse time without the in time zone things at the end or date.today. Something that can help you with this is be sure to use, who uses Rubicup? Okay. How many of you put that into your actual process formally? A few. Good. Cool. I gave a lightning tell a couple of years ago on this and depressingly few people did that. So there are services like Hound, Farsi, there's probably others that I'm codicy that I'm not thinking of or I'm not familiar with. But you can make it a blocker so that you can't merge a PR unless it passes your style check. And amongst the many benefits of that is Rubicup will actually catch some of these errors for you. Any more comments, questions about dates and times? Let's move on to human names. So many of you may have read there were a couple of blog posts that were related. One was called falsehoods programmers believe about names. I believe it was actually inspired by falsehoods programmers believe about time. And to take a few examples from this, we have things like none of these statements are true. People have exactly one canonical full name. Nope. People have exactly one full name which they go by. No. People have at this point in time exactly one canonical full name. People have at this point in time one full name which they go by. No. People's names do not change. That's not true. People's names change but only at a certain enumerated set of events. No. People's names are assigned at birth. Not true. People's names are written in ASCII. Absolutely not true. I'm guessing in this room there's a bunch of people whose names are not actually written in ASCII. Although I'm guessing very few in just emojis yet but that'll come. People's names are written in a single character set. That's not true. People's names are all mapped in Unicode code points. That's not true. Two different systems containing data about the same person will use the same name for that person. That's hopefully pretty obviously not true either. This is terrible. Now I'm feeling a bit crabby. Really the only thing you can do here with names is validate as little as possible. Just don't bother. Why are you trying? Right? Yes, their card holder name probably has to match when you submit the credit card but that's their problem. If you can avoid first name, last name, consider doing so. Just use full name. I have no idea what's going on. Maybe use given name, family name to be a little bit less English specific. Also store things in Unicode. Remember, you can't guarantee real names are used. Don't assume just because you might have a US based and a US centric business that your users will be primarily English speaking or even have as key names. These things are true in the US as well as overseas. Physical addresses. How do you model physical addresses? Right? Yeah. That's good. Well, there are a lot more variations on this than you might expect even in just the United with the United States Postal Service. Even in the US, remember there are rural routes that look like this. There are military addresses that look like that. That doesn't quite fit the city-state paradigm. Remember US Postal Service is Puerto Rico which has a very different address structure. You can actually have a surprising number of lines in a valid address. I saw one example that was supposedly valid that had 12 lines long. It was an international one but still. Basically, don't do this. Until I moved recently, my address had a slash in it. These are actually pretty common in California. They have one halves in numbers. It doesn't validate with Southwest and it doesn't validate with quite a few legacy systems that you'll see out there. Had to have a bunch of banks and things send things to apartment one half. You can standardize these addresses via the US Postal Service. They'll convert them for you and give you some of the right abbreviations. Remember that special characters are still allowed even after that. For example, cities can have apostrophes and addresses can have slashes or dashes and so forth. Things to know about US Postal Codes. Don't use just zip codes. In general, try to use Postal Code which is the international version of the word. Don't just make it five characters long or 10 characters long if you wanted to do plus nine because, again, you're excluding the ability to store addresses from other countries. Including Canada which is close enough you might actually want to be able to ship to it. Also as a bit of trivia, remember that you can't even use zip codes to map to states. There is a database you can buy or possibly download for free that will attempt to give you city information but zip codes not only can cross city boundaries, they can cross state boundaries and here are the ones that currently cross state boundaries. That's all because zip codes map to postal routes, not to geography. It just so happens that most postal routes are geographically constrained. Let's say you want to validate addresses. Again, my first instinct is to say, why are you doing this? Okay, great. The US Postal Service has a database of them. However, these are not always the same as physical addresses. There are entire towns and communities that have no physical addresses in the US Postal Service database. For example, I'm in San Diego, one of the very wealthy suburbs, not suburbs, it's actually a separate city, one of the very wealthy cities in the center of San Diego County is Rancho Santa Fe. The Postal Service delivers everything to their post office and that's it because they didn't want ugly postal trucks driving around going to people's houses. Yet, UPS and Phoenix will deliver to people's houses there. If you're shipping something, maybe you should let them enter their home address even though it's not going to validate. Oh, geez. Any comments, questions about fun stuff with addresses here? Anyone? Bueller? All right, money. Yay. We all need to get paid, right? How do you model money in your Rails apps, database schemas? That's a given. No, correct. Not as a float. I've seen that though. Any other possibilities? That's a good one. And decimal. So I heard those two. Those are sort of the other two approaches that I'm familiar with. So you may use decimal values. That's what a migration doing that would look like. And there are some issues with that. Not that it's invalid. Here we have a line of Ruby code that will render your product for your API. We'll send this out because it's a decimal. It'll look like that when rendered. And now in JavaScript, we do this. What's wrong with that? I'm sorry? Well, maybe. Anything else? Correct. Ding, ding, ding. He said floating point. Current product price in JavaScript is now naively a float. That's how the JSON works. So you can, well, sorry, after that bug came up three different times. So you can get around that. If you want to always remember to use a decimal library on the JavaScript side, there's no problem with that. But inevitably someone will forget to cast to make a new decimal object and the bug will be introduced. Also, you'll get very strange rounding when, for example, you're multiplying by, say, a tax rate or something like that that's maybe has three significant digits. And then suddenly you'll have IEEE rounding issues in your money. Not good. So I prefer the just store sense. Use integers everywhere. You won't have rounding errors. I recommend you keep the name in sense as part of it everywhere and then only convert at the last minute for display purposes to dollars or whatever your currency is. Do be aware some currencies have mills instead of cents. So if that's important, just remember you're multiplying by 1,000 instead. And in this case, usually it's obvious when you've forgotten to convert because it's there on the display. Your totals will be wrong. Easier to test. Email addresses. Those are easy, right? Yeah. So this is now a valid email address. Just keep that in mind. This is now a top level domain. There are still places out there that try to validate.com.net.edu. You can validate that there's an at. There has to be an at sign in an email address and there has to be a dot somewhere in the domain name. That's it. Otherwise you can do the whole verification email dance, right? Which everybody, people know this. But again, people still try to over validate. And it's kind of like, again, why are you doing this? I'll also just mention as a sidebar that for people who aren't aware of this, most email systems, particularly Gmail, will allow you to add a plus after your username. It'll still get delivered to you. But now you can have an infinite number of email addresses without creating new users. This is great for testing or for creating a thousand free trials or whatever you need to do. So internationalization. I'm not going to talk much about internationalization because it's a huge topic. There are literally entire conferences on it. I do have a suggestion which is that particularly if you have a green field application, you've just done Rails new, start putting your hard-coded strings in your config locales just from the beginning. Even if you have no particular plans to go international or support other languages, a cool thing, a cool result of doing that is that one, changing copy is easier. You don't have to search all over the place. It's all in one place. And furthermore, you can even turn over the keys to a product person or a non-dev and they can make copy changes themselves directly in the code instead of handing it off to you. And then you just review their change. Also if you add a locale to your user model and always use it, then again, you won't have to backport or fill this stuff in later. You can just start if you're in the US with USEN or whatever makes sense for you. And then it's there. Payments and credit cards, another huge topic that there's a lot to talk about. Read up on PCI compliance. Again, there's an entire industry around PCI compliance. I think what most people know about it and what you need to take home is never store credit card information, but don't even send it from your client browser to yourself. Use a service that will let you send it directly from the client browser and then give you a web hook back. That way you're not even relying on the browser that somebody's using on an airplane. They're on a Chromebook on an airplane. Just imagine that scenario. And now you're relying on them getting the token and sending it forwarding that to you. No, just use web hooks here. And be sure to consider what happens if the call to your provider, Stripe or whatever, times out. That can happen and depending on your architecture, if you don't use web hooks, you have to actually escalate timeouts to a human being to go and look and see what really happened. Did the call go through or did it fail? Recurring calendar events. That's easy, right? You just have like a day of the week and say, you know, when it recurs or something like that and you're fine, right? Now, read this RFC and consider that most recurring events have no end date. So how are you going to model that? There's an infinite number of them. The rules can get pretty complex. This is a fairly simple one, like every month on the second to the last Thursday. And individual instances of a recurring event can be edited or moved or canceled separately from the actual recurring event. Yeah, now I need this particular body, Mary. That's right. Check out the garage in Seattle. I'd like to just point out that this particular one has as a garnish a second Bloody Mary for you to get through while you're working your way down to the Bloody Mary. And in case you think onion rings, chicken wings, a submarine sandwich, a 12-inch pizza, french fries, and a lime are not enough. Here's the back view. There's a cheeseburger, an onion, a lime, a lemon, and two grilled cheese sandwiches. But you might need this if you start working on recurring events. I highly recommend it. So I talked a little fast, ran a little early, in conclusion, the main takeaway is don't overvalidate. People just have this need to check the values of these things when you can't. And I don't understand it. Even US-only products will have global-like problems. Be culturally aware. Be aware that your experience isn't universal and probably isn't typical. And just don't make assumptions. And there are some references if you get my slides later for the blog post I was talking about. Thank you. I have a request for that slide. Thank you.
 All right, let's get started.  I can't see very well out there, so shout if you feel the need to ask questions or interrupt.  How's the conference for everybody so far?  Good?  All right, excellent.  All right, this thing's not going to work apparently.  So I'm Alex Boster.  I work for Apfolio, which is a company in Santa Barbara.  I work in their San Diego engineering office.  We're hiring.  Come talk to me.  So the last I would have said one particular project inspired this talk, but really it's  also the culmination of the kind of experience you get after a few years of doing web development.  This is a survey of surprisingly difficult things.  What things do I mean?  Commonplace things that you know all about in your day-to-day life, so they're easy to  model and you model them and they're great, and then it turns out it's actually a lot  harder than that.  Things where the obvious implementation may very well cause problems.  And in fact, you know, these are things like timestamps, time zones, physical addresses,  human names.  Now when you hear these terms, what do you think?  Does this sound easy?  These are solved problems, right?  Easy.  No problem.  No complications.  What am I not going to talk about?  I'm not going to talk about cache invalidation or distributed systems or other also surprisingly  difficult things, but this is about real-world stuff.  So one of the things, you know, one of the reasons I wanted to give this talk was that  developers fall into these traps all the time.  We spent months cleaning up old buggy code and then new bugs of the same type are introduced  six months later by other developers.  So maybe, you know, with a laundry list of things to be aware to watch out for, maybe  you won't fall into that trap.  And even very senior developers, you know, it might help to just have the occasional  reminder here.  If you're not a senior developer and you haven't dealt with this stuff much before, then hopefully  this talk will save you some time in the future.  Another good thing is that if you follow these best practices or similar things, then your  app can be more inclusive.  So I know that when I start dealing with these world-world things, you know, it causes me  to want to drink.  Might just drive you to drink, too.  So let's start with time.  So there are a bunch of time and date classes available to you.  The only one I really want to draw attention to, this is mostly for reference later, but  the one I want to draw attention to is the last line where there's just no good cross  system standard for duration.  It's different in every database.  It's different in Ruby than it is in the databases.  So pay a little attention to that and check out active support duration.  But what makes time actually hard to deal with is not this.  It's time zones.  So again, isn't this a solved problem?  You can just have a sufficiently large integer to represent seconds or fractions of a second.  And now you have a time value and you're good, right?  No problems.  Let's all go home.  Well, again, the problem is in time zones.  So how many time zones do you think there are?  Anyone?  Thirty something?  Say it again?  Forty?  Well, you may be right, you know, at a certain level.  Actually the time zone database, which I'll talk about in a minute, defines 385 time zones  and then has a further 176 internal links, which are aliases basically to give them different  names.  So something to remember, there are half hour time zones.  There are quarter hour time zones.  You've got daylight savings time to take into account.  Daylight savings time may start, a place may start observing daylight savings time that  previously didn't, like Arizona currently doesn't.  A place may change their schedule like the entire United States did 10 years ago or so  and shifted when daylight savings time started.  I don't know if this is true currently, but certainly in the past, we've seen examples  of two hour daylight savings time changes.  This was called double summertime in the UK.  And a place may change time zones entirely.  They may just switch.  So this time zone database I spoke of is used in many Unix-like systems.  It's used all over the place with a small band of dedicated developers.  It tracks all geographic time zones since 1970 and they define an area as, you know,  where any two places share the same time zone at the same time at the same time.  So not before, seriously, before 1970, they don't care as much, but they do have historical  data.  And it does track daylight savings time changes.  So an example of this is updated several times a year.  If you think this stuff is static, no.  It's updated several times a year and here's an example of some release notes.  You can read through that quickly.  Mongolia no longer observes daylight savings time.  This region moved from one to another year round and the clocks starting at this particular  time hived off a new area which also affects part of Antarctica.  This change fixed many entries for historical time for Madrid before 1979.  And it noted that Ecuador switched, actually observed daylight savings time in a particular  bit.  The exact details aren't important, but just know this stuff is really complicated and  they go to somebody's keeping track of it.  And that's an example of all the actual regions defined.  That's, again, just unique regions since 1970.  Another little trivia, you know, how many time zones are in the United States according  to this map?  Just the continental United States.  I count at least six.  So we use UTC as a way to standardize things, you know, when things happen at the same instant  in time regardless of what you actually call that time in a particular place.  Hopefully we know that that stands for neither coordinated universal time nor Thomson or  Dursel coordinate, named by diplomats no doubt.  UTC is not a time zone, but every time zone has an offset from UTC.  And as a rule, you should store your time values in UTC.  So also before I proceed, how many, what are the possible offsets from UTC?  UTC is kind of in the middle and you can go forward from it and you can go back.  How far do you, how do they go?  Anyone want to take a guess?  That is exactly what I thought.  So in 1995, Kiribati got tired of having their country in two different days.  So they moved the time zone of some of their outlying islands from minus 10 to plus 14.  So there are plus 14 time zones on that side.  So keep in mind, without a time zone, any time value you have without context could  take place within a 26-hour range, possible half-hour or quarter-hour increments.  So how do you handle this?  Well, if you don't explicitly provide a time zone, a time you provide could be interpreted  using the operating systems default, using your databases default, or using your applications  default time zone.  That should be configured in your Rails app.  Okay, now I need a slightly bigger drink.  So as we said, keep your system and database time in UTC, Rails will store its date times  in UTC, and time zone-aware methods in Rails will use the applications default if you don't  overwrite it with my expressly providing one.  So for example, if you have users, be sure to store a time zone on the user's model  and always use that in your views if you care at all about when things occur for said users.  And just as an example here, you can see that you want to use time.zone.now.  I'll talk about this a little more in a minute.  And ActiveSupport provides some really sophisticated stuff around it.  So don't just use bare Ruby time, use the Rails classes for all this stuff.  So these time zone-aware methods, you can see we're parsing two different times in different  time zones, but they're actually the same time.  So it all works.  That's cool.  And here are some examples of methods you should use.  Hours from now, days ago, those are all good.  Always do time.zone.parse, don't do time.parse.  If you use string parse time in the middle here, always use in time zone at the end  or you will be screwed.  Remember definitely time.current to other methods for getting the current time.  And the UTC ISO 8601 is for if you're providing an API.  Excuse me, if you're providing something to an API.  These examples are all from a stolen shamelessly from a blog post that I note there on the  bottom.  So dates are simpler, right?  Dates don't have a time zone.  How do you know if you should be storing something in a date or a time?  Ask yourself, does it matter what time of day?  This seems really basic, but people make this mistake all the time and just convert a date  to a time willy-nilly.  So don't do this.  So what are some examples of dates?  Well, birthdays.  A day occurs on a day.  We don't generally observe when the actual minute of the day a person was born.  All day calendar events, maybe holidays you might think of.  So for example, I don't know, you know, to take a Western example, Christmas is on the  25th regardless of whether or not you're in Beijing or Toronto or wherever.  So as I said, don't store dates and date times.  You will have problems.  Be very leery of converting back and forth.  You almost never want to do that.  For example, if you, the one case I can think of offhand is if you have a calendar that  you've written and you want to convert, if somebody's editing an event and it goes from  being, say, an all day event to a timed event, then maybe.  So let's see, where are we?  So this is fine.  Where did that come from?  I don't know.  So you want to use date.current because I ran these two lines in the middle of the day,  seconds from each other, and that's what I got.  What happened?  Why did it behave that way?  Anyone?  Sorry?  Yeah, basically.  So I'm going to use date.current.  The other one is basically telling me that in London it's the 24th.  But I see date.today all over the place in code.  So these are really the only two date methods you should be using.  And you absolutely have to avoid time.now, time.parse, time.string.parse time without  the in time zone things at the end or date.today.  Something that can help you with this is be sure to use, who uses Rubicup?  Okay.  How many of you put that into your actual process formally?  A few.  Good.  Cool.  I gave a lightning tell a couple of years ago on this and depressingly few people did  that.  So there are services like Hound, Farsi, there's probably others that I'm codicy that I'm not  thinking of or I'm not familiar with.  But you can make it a blocker so that you can't merge a PR unless it passes your style  check.  And amongst the many benefits of that is Rubicup will actually catch some of these errors for  you.  Any more comments, questions about dates and times?  Let's move on to human names.  So many of you may have read there were a couple of blog posts that were related.  One was called falsehoods programmers believe about names.  I believe it was actually inspired by falsehoods programmers believe about time.  And to take a few examples from this, we have things like none of these statements are true.  People have exactly one canonical full name.  Nope.  People have exactly one full name which they go by.  No.  People have at this point in time exactly one canonical full name.  People have at this point in time one full name which they go by.  No.  People's names do not change.  That's not true.  People's names change but only at a certain enumerated set of events.  No.  People's names are assigned at birth.  Not true.  People's names are written in ASCII.  Absolutely not true.  I'm guessing in this room there's a bunch of people whose names are not actually written  in ASCII.  Although I'm guessing very few in just emojis yet but that'll come.  People's names are written in a single character set.  That's not true.  People's names are all mapped in Unicode code points.  That's not true.  Two different systems containing data about the same person will use the same name for  that person.  That's hopefully pretty obviously not true either.  This is terrible.  Now I'm feeling a bit crabby.  Really the only thing you can do here with names is validate as little as possible.  Just don't bother.  Why are you trying?  Right?  Yes, their card holder name probably has to match when you submit the credit card but  that's their problem.  If you can avoid first name, last name, consider doing so.  Just use full name.  I have no idea what's going on.  Maybe use given name, family name to be a little bit less English specific.  Also store things in Unicode.  Remember, you can't guarantee real names are used.  Don't assume just because you might have a US based and a US centric business that your  users will be primarily English speaking or even have as key names.  These things are true in the US as well as overseas.  Physical addresses.  How do you model physical addresses?  Right?  Yeah.  That's good.  Well, there are a lot more variations on this than you might expect even in just the United  with the United States Postal Service.  Even in the US, remember there are rural routes that look like this.  There are military addresses that look like that.  That doesn't quite fit the city-state paradigm.  Remember US Postal Service is Puerto Rico which has a very different address structure.  You can actually have a surprising number of lines in a valid address.  I saw one example that was supposedly valid that had 12 lines long.  It was an international one but still.  Basically, don't do this.  Until I moved recently, my address had a slash in it.  These are actually pretty common in California.  They have one halves in numbers.  It doesn't validate with Southwest and it doesn't validate with quite a few legacy systems  that you'll see out there.  Had to have a bunch of banks and things send things to apartment one half.  You can standardize these addresses via the US Postal Service.  They'll convert them for you and give you some of the right abbreviations.  Remember that special characters are still allowed even after that.  For example, cities can have apostrophes and addresses can have slashes or dashes and so  forth.  Things to know about US Postal Codes.  Don't use just zip codes.  In general, try to use Postal Code which is the international version of the word.  Don't just make it five characters long or 10 characters long if you wanted to do plus  nine because, again, you're excluding the ability to store addresses from other countries.  Including Canada which is close enough you might actually want to be able to ship to  it.  Also as a bit of trivia, remember that you can't even use zip codes to map to states.  There is a database you can buy or possibly download for free that will attempt to give  you city information but zip codes not only can cross city boundaries, they can cross  state boundaries and here are the ones that currently cross state boundaries.  That's all because zip codes map to postal routes, not to geography.  It just so happens that most postal routes are geographically constrained.  Let's say you want to validate addresses.  Again, my first instinct is to say, why are you doing this?  Okay, great.  The US Postal Service has a database of them.  However, these are not always the same as physical addresses.  There are entire towns and communities that have no physical addresses in the US Postal  Service database.  For example, I'm in San Diego, one of the very wealthy suburbs, not suburbs, it's actually  a separate city, one of the very wealthy cities in the center of San Diego County is Rancho  Santa Fe.  The Postal Service delivers everything to their post office and that's it because they didn't  want ugly postal trucks driving around going to people's houses.  Yet, UPS and Phoenix will deliver to people's houses there.  If you're shipping something, maybe you should let them enter their home address even though  it's not going to validate.  Oh, geez.  Any comments, questions about fun stuff with addresses here?  Anyone?  Bueller?  All right, money.  Yay.  We all need to get paid, right?  How do you model money in your Rails apps, database schemas?  That's a given.  No, correct.  Not as a float.  I've seen that though.  Any other possibilities?  That's a good one.  And decimal.  So I heard those two.  Those are sort of the other two approaches that I'm familiar with.  So you may use decimal values.  That's what a migration doing that would look like.  And there are some issues with that.  Not that it's invalid.  Here we have a line of Ruby code that will render your product for your API.  We'll send this out because it's a decimal.  It'll look like that when rendered.  And now in JavaScript, we do this.  What's wrong with that?  I'm sorry?  Well, maybe.  Anything else?  Correct.  Ding, ding, ding.  He said floating point.  Current product price in JavaScript is now naively a float.  That's how the JSON works.  So you can, well, sorry, after that bug came up three different times.  So you can get around that.  If you want to always remember to use a decimal library on the JavaScript side, there's no  problem with that.  But inevitably someone will forget to cast to make a new decimal object and the bug will  be introduced.  Also, you'll get very strange rounding when, for example, you're multiplying by, say, a  tax rate or something like that that's maybe has three significant digits.  And then suddenly you'll have IEEE rounding issues in your money.  Not good.  So I prefer the just store sense.  Use integers everywhere.  You won't have rounding errors.  I recommend you keep the name in sense as part of it everywhere and then only convert  at the last minute for display purposes to dollars or whatever your currency is.  Do be aware some currencies have mills instead of cents.  So if that's important, just remember you're multiplying by 1,000 instead.  And in this case, usually it's obvious when you've forgotten to convert because it's  there on the display.  Your totals will be wrong.  Easier to test.  Email addresses.  Those are easy, right?  Yeah.  So this is now a valid email address.  Just keep that in mind.  This is now a top level domain.  There are still places out there that try to validate.com.net.edu.  You can validate that there's an at.  There has to be an at sign in an email address and there has to be a dot somewhere in the  domain name.  That's it.  Otherwise you can do the whole verification email dance, right?  Which everybody, people know this.  But again, people still try to over validate.  And it's kind of like, again, why are you doing this?  I'll also just mention as a sidebar that for people who aren't aware of this, most email  systems, particularly Gmail, will allow you to add a plus after your username.  It'll still get delivered to you.  But now you can have an infinite number of email addresses without creating new users.  This is great for testing or for creating a thousand free trials or whatever you need  to do.  So internationalization.  I'm not going to talk much about internationalization because it's a huge topic.  There are literally entire conferences on it.  I do have a suggestion which is that particularly if you have a green field application, you've  just done Rails new, start putting your hard-coded strings in your config locales just from  the beginning.  Even if you have no particular plans to go international or support other languages,  a cool thing, a cool result of doing that is that one, changing copy is easier.  You don't have to search all over the place.  It's all in one place.  And furthermore, you can even turn over the keys to a product person or a non-dev and  they can make copy changes themselves directly in the code instead of handing it off to you.  And then you just review their change.  Also if you add a locale to your user model and always use it, then again, you won't have  to backport or fill this stuff in later.  You can just start if you're in the US with USEN or whatever makes sense for you.  And then it's there.  Payments and credit cards, another huge topic that there's a lot to talk about.  Read up on PCI compliance.  Again, there's an entire industry around PCI compliance.  I think what most people know about it and what you need to take home is never store  credit card information, but don't even send it from your client browser to yourself.  Use a service that will let you send it directly from the client browser and then give you  a web hook back.  That way you're not even relying on the browser that somebody's using on an airplane.  They're on a Chromebook on an airplane.  Just imagine that scenario.  And now you're relying on them getting the token and sending it forwarding that to you.  No, just use web hooks here.  And be sure to consider what happens if the call to your provider, Stripe or whatever,  times out.  That can happen and depending on your architecture, if you don't use web hooks, you have to actually  escalate timeouts to a human being to go and look and see what really happened.  Did the call go through or did it fail?  Recurring calendar events.  That's easy, right?  You just have like a day of the week and say, you know, when it recurs or something like  that and you're fine, right?  Now, read this RFC and consider that most recurring events have no end date.  So how are you going to model that?  There's an infinite number of them.  The rules can get pretty complex.  This is a fairly simple one, like every month on the second to the last Thursday.  And individual instances of a recurring event can be edited or moved or canceled separately  from the actual recurring event.  Yeah, now I need this particular body, Mary.  That's right.  Check out the garage in Seattle.  I'd like to just point out that this particular one has as a garnish a second Bloody Mary for  you to get through while you're working your way down to the Bloody Mary.  And in case you think onion rings, chicken wings, a submarine sandwich, a 12-inch pizza,  french fries, and a lime are not enough.  Here's the back view.  There's a cheeseburger, an onion, a lime, a lemon, and two grilled cheese sandwiches.  But you might need this if you start working on recurring events.  I highly recommend it.  So I talked a little fast, ran a little early, in conclusion, the main takeaway is don't  overvalidate.  People just have this need to check the values of these things when you can't.  And I don't understand it.  Even US-only products will have global-like problems.  Be culturally aware.  Be aware that your experience isn't universal and probably isn't typical.  And just don't make assumptions.  And there are some references if you get my slides later for the blog post I was talking  about.  Thank you.  I have a request for that slide.  Thank you.
A_Survey_of_Surprisingly_Difficult_Things_2_0_2075.png A_Survey_of_Surprisingly_Difficult_Things_2_2076_2147.png A_Survey_of_Surprisingly_Difficult_Things_2_2148_2423.png A_Survey_of_Surprisingly_Difficult_Things_2_2424_2507.png A_Survey_of_Surprisingly_Difficult_Things_2_2508_2723.png A_Survey_of_Surprisingly_Difficult_Things_2_2724_3215.png A_Survey_of_Surprisingly_Difficult_Things_2_3216_3935.png A_Survey_of_Surprisingly_Difficult_Things_2_3936_3995.png A_Survey_of_Surprisingly_Difficult_Things_2_3996_4643.png A_Survey_of_Surprisingly_Difficult_Things_2_4644_4703.png A_Survey_of_Surprisingly_Difficult_Things_2_4704_6635.png A_Survey_of_Surprisingly_Difficult_Things_2_6636_8639.png A_Survey_of_Surprisingly_Difficult_Things_2_8640_8723.png A_Survey_of_Surprisingly_Difficult_Things_2_8724_9503.png A_Survey_of_Surprisingly_Difficult_Things_2_9504_9875.png A_Survey_of_Surprisingly_Difficult_Things_2_9876_10007.png A_Survey_of_Surprisingly_Difficult_Things_2_10008_10247.png A_Survey_of_Surprisingly_Difficult_Things_2_10248_12155.png A_Survey_of_Surprisingly_Difficult_Things_2_12156_13187.png A_Survey_of_Surprisingly_Difficult_Things_2_13188_15983.png A_Survey_of_Surprisingly_Difficult_Things_2_15984_16031.png A_Survey_of_Surprisingly_Difficult_Things_2_16032_16451.png A_Survey_of_Surprisingly_Difficult_Things_2_16452_16799.png A_Survey_of_Surprisingly_Difficult_Things_2_16800_20051.png A_Survey_of_Surprisingly_Difficult_Things_2_20052_20339.png A_Survey_of_Surprisingly_Difficult_Things_2_20340_21935.png A_Survey_of_Surprisingly_Difficult_Things_2_21936_22223.png A_Survey_of_Surprisingly_Difficult_Things_2_22224_23699.png A_Survey_of_Surprisingly_Difficult_Things_2_23700_23927.png A_Survey_of_Surprisingly_Difficult_Things_2_23928_28139.png A_Survey_of_Surprisingly_Difficult_Things_2_28140_29819.png A_Survey_of_Surprisingly_Difficult_Things_2_29820_32255.png A_Survey_of_Surprisingly_Difficult_Things_2_32256_33371.png A_Survey_of_Surprisingly_Difficult_Things_2_33372_34379.png A_Survey_of_Surprisingly_Difficult_Things_2_34380_35303.png A_Survey_of_Surprisingly_Difficult_Things_2_35304_38303.png A_Survey_of_Surprisingly_Difficult_Things_2_38304_41627.png A_Survey_of_Surprisingly_Difficult_Things_2_41628_44003.png A_Survey_of_Surprisingly_Difficult_Things_2_44004_44327.png A_Survey_of_Surprisingly_Difficult_Things_2_44328_49031.png A_Survey_of_Surprisingly_Difficult_Things_2_49032_49065.png