At some point in time, you will be given a task like “What happened to X? Why can I no longer see it?” or “Why does the date on X say ‘Dec. 31’ when it previously said ‘Jan. 1’?” With any sort of complex system, these kinds of questions are inevitable. A user changes something without telling another user, or a weird process messes with data, or worse yet, a bug happens (which is almost inevitable).
With if you are properly logging an audit trail, these kinds of questions become really easy to solve. Want to know who deleted X? Check the logs. The logs don’t even need to contain all the detail – just a general idea. For example “User ‘Gordon Freeman’ deleted X on Aug. 1, 2012” is detailed enough to tell you who did it, and when it happened. It’s so much easier to add some basic logging in at the start, then to spend time trying to track theses kind of things down after the fact.
Save yourself some time. Add logging/auditing right now.
Have you ever been browsing a website, viewing a list of items, and had it display 20 out of 21 items? Don’t you just love clicking the “next” button to see one more item? I don’t.
Pagination – the idea of breaking up data into pages of a set length – is often necessary. When querying data, and not knowing exactly how much data is going to come back, you need to put a limit on things. Imagine searching Google, and it loading up a web page with all 550,000+ results for “discount Koala meat”. I’m pretty sure that would crash any browser – especially on a memory/CPU limited mobile device. So rather than returning all results, it breaks the search results into pages of a reasonable size. But what is a reasonable size?
One frustration I have with sites like Amazon.com is that they will display page sizes of 20 items or so. But what if my query returns 21 items? Do I really want to make the user click the “next” button to see a single item? What if they want to see items on both pages at the same time?
Situations like this make me want to implement some form of fuzzy pagination. If n is the page being requested, we examine page n+1 (if it exists). If page n+1 does exist and is the last page, and the number of results on page n+1 is <= 50% of the page size, we return the results as part of the request. If any of the above conditions fail (there is no next page, or the next page is >50% the size of a regular page), we don’t do anything special – we just return the regular sized page.
It’s features like this that are almost completely transparent to the end user, but make it that much better of a user experience.
Sometimes, when working on something, you start digging into a particular technical issue. The more you dig into it, the more it leads you away from your original goal. I like to call this “chasing the rabbit”. If you chase a rabbit through the woods, and only keep your eyes on the rabbit, you’ll eventually find yourself lost in the woods.
It’s easy to lose sight of the big picture when you are “chasing the rabbit”.
I hope I never grow tired of learning new things.
In some respects, this is great. I never grow bored of life. There’s far too many interesting things out there. The world is filled with interesting problems, and even more interesting solutions.
In other respects, it is a bit of a curse. Because of this, I have far too many interests. As the saying goes, “jack of all trades, master of none”. I sometimes feel like I don’t have the depth of knowledge that I’d like in particular subjects.
It’s amazing how deep you can go down the rabbit hole. Let’s just take programming as an example. For my day job I work with Python and Django, and in my spare time at home I occasionally play around with C# and ASP.Net. There’s a ton of interesting technologies out there. For example, suppose you are shooting for more performance for a web app, so you start digging into caching. So you start learning about Redis, and other key-value stores. That leads you down the path of learning about cache misses, specific implementations of hash tables, and fast hashing algorithms. That area of computer science alone has claimed many a PhD student. 😛
I think my ultimate dream job would be where I get paid to learn, and on occasion, get to apply/unleash that knowledge on an interesting problem.
The rope bridge philosophy is something that I discovered over at GameDev.Net a long time ago, so I can’t take total credit for it. It goes something like this:
When you need to cross a river, you first build a rope bridge. You don’t start with a concrete and steel bridge, because you don’t know if you will be crossing over that same river again. By starting with just a rope bridge, you’ve spent minimal resources on a problem that you aren’t guaranteed to encounter again. If you find yourself needing to cross that same bridge multiple times, then you can go and revise/rebuild the bridge with something stronger. Yes, it means you’ve built the same bridge twice, but you’ve also saved yourself resources by only building what is necessary.
This is totally applicable in programming – especially something like game development. There is often no clear “best” solution, and you face a huge number of unknown problems. You don’t know if the game you have designed is going to be fun without getting something working first. You only want to spend resources where they are going to matter.
Eventually, as you gain more experience, you learn when to preemptively built a bigger bridge. Like anything else, a bit of that intuition comes from experience. But even then, you still risk the danger of spending resources where they won’t be needed.
Something that I often tell my team is the following:
1. Make it.
2. Make it work.
3. Make it work right.
4. Make it work right fast.
Building a bigger bridge before you’ve gotten to your destination is making something work right/fast before the entire thing works. Learning when to build something bigger than a rope bridge at start is a bit of an art. It takes experience – but experience only comes at the cost of making mistakes. You also need the freedom to go back and revise/refactor things once everything is working. This is something that you often have to push for, and something that management needs to understand.
I think there’s something that society keeps forgetting:
You are free to choose your actions, but you aren’t free to choose the consequences.
Far too often people attempt to make up excuses on why they should somehow be exempt from whatever negative consequences they are experiencing:
“It came from my primal/animal instinct.” (Are you saying you are no better than an animal?)
“Someone else told me to do it.” (If someone else told you to jump off a bridge, would you?)
“I was drunk at the time.” (Who’s choice was it to drink in the first place?)
Think of the law of gravity. It has some pretty severe consequences. If you ignore the law, you aren’t free from its affects. You can’t simply wish that gravity isn’t there, and have it go away.
We often think of constraints as being a bad thing. Far often we don’t recognize that constraints force us to grow.
Let’s say you are writing a video chat program. With no restrictions, you would likely require a gigabit internet connection with zero lost packets and a sub 50 millisecond ping, and you’d write it in whatever language you want. But let’s say that you now have a set of restrictions on it: suppose that it has to run on a mobile device.
- You will likely be CPU limited, bandwidth limited, and have a maximum camera resolution that you can use (as well as a maximum display resolution).
- You will have to be aware of power consumption and battery life.
- You will likely be restricted to a subset of programming languages and APIs.
Restrictions like this are going to force you to be more efficient and more creative in how you solve the original problem. As a result, whatever solution you end up creating will likely be a lot more interesting than if you had unlimited CPU/bandwidth.
I have a huge amount of respect for those early programmers that had to deal with very tight constraints who still managed to create some amazing programs and games.