00:00:12.679
Alright, let's get started! I can't see very well out there, so please shout if you feel the need to ask questions or interrupt.
00:00:18.300
How's the conference for everybody so far? Good? Alright, excellent! This thing's not going to work apparently, so I'm Alex Boster. I work for AppFolio, which is a company in Santa Barbara, and I work in their San Diego engineering office.
00:00:31.080
We're hiring, so come talk to me! The last project I would have said 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.
00:00:43.649
So, this is a survey of surprisingly difficult things. Now, what do I mean by 'things'? I mean commonplace issues that you encounter in your day-to-day life.
00:00:57.809
They seem easy to model at first, and they seem great, but then it turns out that they can actually be a lot harder than anticipated. Things where the obvious implementation may very well cause problems.
00:01:11.670
These are things like timestamps, time zones, physical addresses, and human names. Now, when I mention these terms, what do you think? Does this sound easy? These are solved problems, right? Easy, no problem, no complications.
00:01:22.770
What I'm not going to talk about today are cache invalidation, distributed systems, or other surprisingly difficult things; instead, I'll focus on real-world scenarios. One of the main reasons I wanted to give this talk is that developers fall into these traps all the time.
00:01:51.570
We spend months cleaning up old buggy code, only for new bugs of the same kind to be introduced six months later by other developers. So with a laundry list of things to watch out for, hopefully, you won't fall into that trap.
00:02:05.969
Even very senior developers can benefit from an occasional reminder. If you're not a senior developer and haven't dealt with these issues much before, I hope this talk saves you some time in the future.
00:02:18.540
Another good point is that if you follow these best practices, you will likely be more inclusive. I know that when I start dealing with real-world challenges, it might drive me to drink; it could do the same for you.
00:02:33.880
So, let's start with time. There are a bunch of time and date classes available to you. The only one I really want to draw attention to is for reference later. There's no good cross-system standard for dealing with time.
00:02:49.750
It's different in every database, and it varies from Ruby to those databases. So pay a little attention to that and check out Active Support Duration. But what makes time actually hard to deal with is the time zones.
00:03:06.190
Isn't this a solved problem? You can use a sufficiently large integer to represent seconds or fractions of a second, and now you have a time value, right? No problems? Let's all go home! Well, the problem is with time zones.
00:03:41.640
How many time zones do you think exist? A few dozen? You might be right at a superficial level, but the time zone database defines three hundred and eighty-five time zones, with an additional hundred and seventy-six internal links that are aliases for different names.
00:04:47.800
Remember that there are half-hour time zones, quarter-hour time zones, and daylight savings time to account for. Places may start observing daylight savings time that previously didn’t, like Arizona, which currently does not observe it. Regions may change their schedule, as the entire United States did ten years ago.
00:05:13.169
There have been cases of temporary daylight savings time changes, like double summer time in the UK, or a place may simply switch time zones entirely. The time zone database I mentioned is used in many Unix-like systems and tracks all geographic time zones since 1970.
00:05:40.599
They define an area as a location where any two places share the same time zone. So, before the 1970s, this database didn’t pay much attention, but they do have historical data and account for daylight savings time changes.
00:06:28.610
The example of this database is updated several times a year. If you think this stuff is static, it's not. Just recently, commute changes happened, such as Mongolia no longer observing daylight savings time.
00:07:09.790
These changes are quite illustrative. Ecuador switched to observing daylight savings time on a particular day, and other areas had adjustments too. So, just remember that this stuff is complicated, and thank goodness someone’s keeping track of it.
00:07:23.800
Interesting trivia: how many time zones are in the continental United States? I count at least six! We use UTC to standardize events regardless of what you actually call that time in a particular place.
00:07:46.210
Organized by Coordinated Universal Time (UTC), which is not a time zone, but every time zone has an offset from UTC. As a rule, you should store your time values in UTC. What are possible offsets from UTC? Well, keep in mind that in 1995, Kiribati moved some of its islands from -10 to +14, creating quite a few unique offsets.
00:08:46.430
Without a specified time zone, any time value lacks context and could fall within a range of twenty-six hours with possible half or quarter-hour increments. How do you manage this? If you don't explicitly provide a time zone, the time can be interpreted using the operating system’s default, the database’s default, or the application’s default time zone.
00:09:20.000
Before I proceed, keep in mind to 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 application’s default unless you provide one explicitly.
00:10:08.510
For example, if you have users, make sure to store their time zone in the user model and always use that in your views if you care about when events occur for those users. Always prefer the time zone methods available in ActiveSupport.
00:10:41.080
For various time-related tasks, it's important to use the Rails classes rather than raw Ruby time. This way, the conversion is handled for you, and you get consistent output. Here are some methods you should be using: 'hours from now', 'days ago', and so forth. Always remember to use 'in time zone' methods to avoid confusion.
00:11:39.370
Dates are a bit simpler since they don't carry a time zone, but you need to be wary of whether you should be storing something as a date or a timestamp. Ask yourself if it matters what time of day it is. This may seem basic, but people make this mistake regularly.
00:12:07.470
For example, a birthday is a date that occurs on a special day, regardless of the time the person was born. Calendar events, like holidays, are similar, where the occurrence is solely based on the date.
00:12:48.030
So, don't store dates as date-times if you want to avoid complications! Be wary of converting back and forth unless you really need to, such as when switching an all-day event to a timed one.
00:13:12.620
Be sure to use 'date.current' for current date identification, as using 'time.now' can yield context issues based on the server's time zone.
00:13:49.370
When it comes to human names, people often have complex naming situations. For example, the falsehoods programmers believe about names include things like assuming everyone has one full name or that names are assigned at birth.
00:14:48.450
In reality, people can change their names, use different characters in their names, and so forth. Thus, I encourage you to validate names as little as possible and consider avoiding the first-name last-name formats whenever feasible!
00:15:40.800
Moving on to addresses, even in the United States, there is more variation in modeling those than you might expect. There are rural routes, military addresses, and areas such as Puerto Rico that have their own unique structures.
00:16:29.680
You might encounter addresses with slashes, dashes, or other special characters, and some cities have names with apostrophes. When validating addresses, don't be too rigid because physical addresses may not always align with postal addresses.
00:17:26.500
For instance, in some wealthier areas, deliveries might just go to a post office instead of residential addresses, complicating things for shipping companies.
00:18:11.720
Regarding money, don’t store values as floats. Use integers wherever possible to avoid rounding errors and only convert for display when you need to.
00:19:05.730
Email addresses are tricky. Many systems try to adhere to standards, although it is generally accepted that any address with an @ symbol is valid. Furthermore, Gmail users can add ‘+’ to their usernames, generating infinite variants of their email when testing.
00:19:49.700
Internationalization is a massive topic, but if you're starting a new application, consider putting hardcoded strings in your config locales from the beginning. It's easier to manage copy changes and allows non-developers to make edits directly.
00:20:28.420
When handling payments and credit cards, be sure to follow PCI compliance. Don’t store credit card information yourself; use services that allow you to pass tokens instead, and make sure to account for all potential timeouts in your system.
00:21:11.900
Recurring calendar events need to be treated thoughtfully, acknowledging that many recurring events might not have a specific end date. The rules for these events can be complex, and you need to handle instances individually.
00:22:09.860
In conclusion, remember not to over-validate. There's often this impulse to check values excessively, but keep in mind that every user base may have different expectations and contexts.
00:23:05.830
Be culturally aware—your experience isn't universal, and not everyone fits into rigid categories. Finally, thank you for your attention, and here are some references for further reading.