Recently I have been working in CoffeeScript a bit. One of the many nice parts of CoffeeScript is Cake, a lightweight build system that ships with CoffeeScript. If your Cakefile starts to get a bit long, it is a piece of cake (no pun intended) to split it up into more manageable... uh... slices? (Okay, sorry, no more bad cake jokes)
Now anytime you want to define a new task you can put it in its own file in the tasks folder rather than fattening up your Cakefile. If you feel like being extra fancy, check out wrench which will let you load tasks recursively.
Friday, April 19, 2013
CoffeeScript Tip: Modular Cakefile
Friday, September 14, 2012
How we sidestepped an App Store catastrophe
UPDATE: The app has been approved since I originally wrote this post. The app review delay had nothing to do with technology choices, and Apple has been fairly quick to approve updates since.
I submitted Outbox's iPad app for review on July 10. It went into "In Review" status on July 24, where it has remained ever since. Actually, it was rejected once on August 22 because the demo account credentials I gave didn't work, despite server logs showing 2 logins on the demo account from Cupertino. I resubmitted, saw 4 logins the following day, and have heard nothing since. My expedited review request was denied. The request status update feature in iTunes Connect is a joke because it takes a week to get a response that the app is still in review, which of course I can see in iTunes Connect. Apple Developer Relations can't help because they can only contact the review team through the same channels I can. One of Outbox's founders even contacted a handful of friendly VPs at Apple for help, and still nothing.
So here we are 2 months later (and counting), still in review.
For a while we continued to plan our launch for "after we get approval" thinking that the app was bound to be approved soon since it had already been so much longer than the average review time, but the weeks kept slipping by and we were forced to delay our launch week after week. Finally, we came to terms with the fact that Apple will get around to approving (or rejecting) the app whenever they damn well feel like it. It could be tomorrow or it could be a year from now. The important lesson I learned is if your product lives in the iOS App Store you are forfeiting control over your release schedule. Have a backup plan.
Fortunately the Outbox iPad app is built with Javascript, HTML, and CSS. The app calls into native extensions for functionality like contacts and notifications as needed through PhoneGap, and makes heavy use of hardware accelerated transforms to get a native look and feel. So after a short discussion, we decided to launch a web app with support for iPad only (we haven't optimized for different aspect ratios or mouse interaction yet). This required almost zero additional effort because this is how we test the app most of the time anyway. The native bits that the app depends on degrade gracefully in a browser when they are not available. The only thing that needed to change was the email confirmation at the end of the signup process which now says "go to this URL on your iPad" instead of "download the app from the App Store". Our designer whipped up a screen with some instructions for the user to pin the app to the home screen when they follow the link from the confirmation email:
In some ways the web app experience is actually better than the app store version of the app. Nitro is available in chromeless web apps, but not in UIWebView, so Javascript execution gets about a 5X speed boost. At any rate, launching the web app beats the hell out of sitting around waiting when we could be making money and iterating on the product. Had we built a fully native application to begin with, turning around to build a web app would likely have taken weeks, if not longer. And if nothing else, it feels good to own our release schedule again.
We just launched in Austin, so if you live in Austin, try us out!
I submitted Outbox's iPad app for review on July 10. It went into "In Review" status on July 24, where it has remained ever since. Actually, it was rejected once on August 22 because the demo account credentials I gave didn't work, despite server logs showing 2 logins on the demo account from Cupertino. I resubmitted, saw 4 logins the following day, and have heard nothing since. My expedited review request was denied. The request status update feature in iTunes Connect is a joke because it takes a week to get a response that the app is still in review, which of course I can see in iTunes Connect. Apple Developer Relations can't help because they can only contact the review team through the same channels I can. One of Outbox's founders even contacted a handful of friendly VPs at Apple for help, and still nothing.
So here we are 2 months later (and counting), still in review.
For a while we continued to plan our launch for "after we get approval" thinking that the app was bound to be approved soon since it had already been so much longer than the average review time, but the weeks kept slipping by and we were forced to delay our launch week after week. Finally, we came to terms with the fact that Apple will get around to approving (or rejecting) the app whenever they damn well feel like it. It could be tomorrow or it could be a year from now. The important lesson I learned is if your product lives in the iOS App Store you are forfeiting control over your release schedule. Have a backup plan.
Fortunately the Outbox iPad app is built with Javascript, HTML, and CSS. The app calls into native extensions for functionality like contacts and notifications as needed through PhoneGap, and makes heavy use of hardware accelerated transforms to get a native look and feel. So after a short discussion, we decided to launch a web app with support for iPad only (we haven't optimized for different aspect ratios or mouse interaction yet). This required almost zero additional effort because this is how we test the app most of the time anyway. The native bits that the app depends on degrade gracefully in a browser when they are not available. The only thing that needed to change was the email confirmation at the end of the signup process which now says "go to this URL on your iPad" instead of "download the app from the App Store". Our designer whipped up a screen with some instructions for the user to pin the app to the home screen when they follow the link from the confirmation email:
In some ways the web app experience is actually better than the app store version of the app. Nitro is available in chromeless web apps, but not in UIWebView, so Javascript execution gets about a 5X speed boost. At any rate, launching the web app beats the hell out of sitting around waiting when we could be making money and iterating on the product. Had we built a fully native application to begin with, turning around to build a web app would likely have taken weeks, if not longer. And if nothing else, it feels good to own our release schedule again.
We just launched in Austin, so if you live in Austin, try us out!
Monday, September 3, 2012
My first "Hacker News Effect" experience
I recently ended a long quiet period on my blog. It had been 9 months since my last post, which incidentally, is about how old my daughter is... I'm sure there's correlation there somewhere. Anyway, as I sat around feeling like a bad citizen of the internet, I decided to dust off a post about mobile web performance I had been working on for a while and finally publish it. I also figured I might as well try submitting the post to Hacker News. I checked the pickup rate and saw that it was a "good time" to submit, so I did, fully expecting to get just a handful of extra views before the submission quickly and quietly dropped off the newest submissions list.
About 10 minutes later I checked my traffic statistics and saw I had almost 200 views. I checked Hacker News and saw my submission was in the #5 spot on the front page. My wife and I spent the next 2 hours watching the submission rise to the top spot while racking up a little over 3,000 views. I actually had trouble sleeping that night! But not the bad, anxious kind of sleeplessness, it was lack of sleep due to excitement!
By the time my submission dropped off the front page my post had received about 15,000 views. I never promoted my own writing before so my blog received a steady trickle of organic search traffic. Hacker News made my previous traffic graph look like a flat line.
The initial spike has long since ended, but the post is still getting around 100 views per hour, which is head and shoulders above what my blog used to get!
My biggest surprise throughout the whole experience was how overwhelmingly positive the feedback was in both the discussion on Hacker News and in the comments on the post. Here was my favorite:
About 10 minutes later I checked my traffic statistics and saw I had almost 200 views. I checked Hacker News and saw my submission was in the #5 spot on the front page. My wife and I spent the next 2 hours watching the submission rise to the top spot while racking up a little over 3,000 views. I actually had trouble sleeping that night! But not the bad, anxious kind of sleeplessness, it was lack of sleep due to excitement!
By the time my submission dropped off the front page my post had received about 15,000 views. I never promoted my own writing before so my blog received a steady trickle of organic search traffic. Hacker News made my previous traffic graph look like a flat line.
The initial spike has long since ended, but the post is still getting around 100 views per hour, which is head and shoulders above what my blog used to get!
My biggest surprise throughout the whole experience was how overwhelmingly positive the feedback was in both the discussion on Hacker News and in the comments on the post. Here was my favorite:
I already feel like you saved me 2 weeks in testing.Thank you Hacker News, and thank you to everyone who stopped by to read!
Saturday, September 1, 2012
10 tips for getting that native iOS feel with PhoneGap
At Outbox we have been hard at work building our iPad application with PhoneGap. I wanted to share some of the lessons my team has learned so far. All of the following tips have been tested on iOS 4, 5, and 6 with PhoneGap 1.8. Also note that these tips apply just as well to web applications running on mobile Safari.
Want more information about developing high quality PhoneGap applications, with in-depth explanations and working code examples? Check out phonegap-tips.com.
Tip #1 - Test on old hardware
Your app looks and feels great on that shiny new iPad 3 or iPhone 4S, right? Unfortunately, not everyone runs out to drop $500 whenever Apple releases a new toy. Get your hands on a first generation iPad or third generation iPhone. Always test on them first. If your app performs acceptably on on old hardware it is going to be blazing fast on the newer hardware.
Tip #2 - Use pre-emptive 3D transforms
If an element is going to be transformed in response to user interaction put an identity 3D transform on the element before any user interaction happens, for example:
Tip #3 - Show or hide elements with 3D transforms when possible
Invariably you will have some elements that need to be shown or hidden based on user interaction. For example, a modal dialog or menu of some sort. The easiest way to do this is usually display:none or visibility:hidden, however, you can achieve a significant speed increase by using a 3D transform to position the element offscreen. In my experience, using this technique to show or hide menus resulted in a 3-5X speed increase over display:none.
Tip #4 - Preload images
Even though your images are already stored on the iOS device there will still be a noticeable "pop-in" when the images are first displayed. I found CSS3 caching to be the best method for dealing with this issue. If you are not keen on enumerating and maintaining a list of every background image you use in your app, here is a jQuery plugin that will preload any background image referenced in your CSS.
Tip #5 - Phark image replacement does not play nice with 3D transforms
Image replacement by setting a large, negative text-indent is great for accessibility since screen readers will find the text and browsers will only display the image. However, the text-indent will make for unbearably choppy 3D transitions in mobile Safari (this is actually related to tip #7). Fortunately, there are alternatives to Phark that perform much better.
Tip #6 - Velocity scrolling!
Velocity scrolling is a super easy way to get a native feel in the scrollable portions of your app. It is simple to enable it on any scrollable area with a CSS rule:
Tip #7 - Respect maximum texture sizes
Each iOS device has a maximum texture size. If you try to smoothly transform an element larger than the maximum texture size, the element will be broken into smaller tiles before the transform is applied, and this a very slow and ugly process.
Tip #8 - Hide large, off-screen images
If your app displays many large images, hide the images that are off-screen. Otherwise, it doesn't take many large images to unceremoniously crash your app. Both display:none and visibility:hidden work just fine, but tip #3 will not cut it here.
Tip #9 - Eliminate tap event delays
It takes mobile Safari about a third of a second (300 milliseconds) to decide that a touch start event followed by a touch end event should be synthesized into a click event. Plenty of frameworks, such as jQuery Mobile, have "tap" handling code that can eliminate this delay for you.
Tip #10 - Disable tap highlighting
Update
Want more information about developing high quality PhoneGap applications, with in-depth explanations and working code examples? Check out phonegap-tips.com.
Tip #1 - Test on old hardware
Your app looks and feels great on that shiny new iPad 3 or iPhone 4S, right? Unfortunately, not everyone runs out to drop $500 whenever Apple releases a new toy. Get your hands on a first generation iPad or third generation iPhone. Always test on them first. If your app performs acceptably on on old hardware it is going to be blazing fast on the newer hardware.
Tip #2 - Use pre-emptive 3D transforms
If an element is going to be transformed in response to user interaction put an identity 3D transform on the element before any user interaction happens, for example:
-webkit-transform: translate3d(0px,0px,0px);When the user first interacts with the element the initial movement will be much smoother.
Tip #3 - Show or hide elements with 3D transforms when possible
Invariably you will have some elements that need to be shown or hidden based on user interaction. For example, a modal dialog or menu of some sort. The easiest way to do this is usually display:none or visibility:hidden, however, you can achieve a significant speed increase by using a 3D transform to position the element offscreen. In my experience, using this technique to show or hide menus resulted in a 3-5X speed increase over display:none.
Tip #4 - Preload images
Even though your images are already stored on the iOS device there will still be a noticeable "pop-in" when the images are first displayed. I found CSS3 caching to be the best method for dealing with this issue. If you are not keen on enumerating and maintaining a list of every background image you use in your app, here is a jQuery plugin that will preload any background image referenced in your CSS.
Tip #5 - Phark image replacement does not play nice with 3D transforms
Image replacement by setting a large, negative text-indent is great for accessibility since screen readers will find the text and browsers will only display the image. However, the text-indent will make for unbearably choppy 3D transitions in mobile Safari (this is actually related to tip #7). Fortunately, there are alternatives to Phark that perform much better.
Tip #6 - Velocity scrolling!
Velocity scrolling is a super easy way to get a native feel in the scrollable portions of your app. It is simple to enable it on any scrollable area with a CSS rule:
-webkit-overflow-scrolling: touch;That's it! However, be warned, Apple's current implementation is a little bit buggy.
Tip #7 - Respect maximum texture sizes
Each iOS device has a maximum texture size. If you try to smoothly transform an element larger than the maximum texture size, the element will be broken into smaller tiles before the transform is applied, and this a very slow and ugly process.
Tip #8 - Hide large, off-screen images
If your app displays many large images, hide the images that are off-screen. Otherwise, it doesn't take many large images to unceremoniously crash your app. Both display:none and visibility:hidden work just fine, but tip #3 will not cut it here.
Tip #9 - Eliminate tap event delays
It takes mobile Safari about a third of a second (300 milliseconds) to decide that a touch start event followed by a touch end event should be synthesized into a click event. Plenty of frameworks, such as jQuery Mobile, have "tap" handling code that can eliminate this delay for you.
Tip #10 - Disable tap highlighting
Have you ever noticed that translucent gray highlight mobile Safari puts on links and buttons when they are clicked? It is a dead giveaway that your app is not native, but you can get rid of it by making the highlight color completely transparent:
-webkit-tap-highlight-color:rgba(0,0,0,0);Overall we're loving PhoneGap at Outbox. Hopefully these tips will help make a few more happy PhoneGappers!
Update
This post got picked up by Hacker News. See the discussion here.
Saturday, December 10, 2011
Zero-friction Javascript Minification with RequireJS and Express
Most software development efforts involve multiple source code files, and Javascript-heavy web applications are no exception. However, if you want your application to load fast, then you need to combine and minify all those Javascript files somehow. RequireJS is a library that can help with this process by allowing you to define javascript modules and the dependencies between them. In "development mode", RequireJS will download modules individually as they are needed. When all of your scripts are shiny and bug-free you can run the optimization tool which will analyze the module dependencies and create a single, minified Javascript file.
The optimization tool is usually run as part of a build process before deploying the application, but if your application runs on Node.js you can eliminate this extra step. Since the optimization tool is itself a Node.js script, it can be invoked directly from the application's start-up code. Here is what this might look like with the fantastic Express web framework:
In development mode, the application will serve the non-optimized Javascript files from the public folder. In production mode, the RequireJS optimizer will combine and minify the Javascript files and serve the optimized files from the public_build folder. Any other files in the public folder (such as images and stylesheets) will also be copied to the public_build folder, so you don't need to clutter your source code repository with generated files.
The optimization tool is usually run as part of a build process before deploying the application, but if your application runs on Node.js you can eliminate this extra step. Since the optimization tool is itself a Node.js script, it can be invoked directly from the application's start-up code. Here is what this might look like with the fantastic Express web framework:
In development mode, the application will serve the non-optimized Javascript files from the public folder. In production mode, the RequireJS optimizer will combine and minify the Javascript files and serve the optimized files from the public_build folder. Any other files in the public folder (such as images and stylesheets) will also be copied to the public_build folder, so you don't need to clutter your source code repository with generated files.
Sunday, December 4, 2011
Commit early, commit often
Hidden deep inside TortoiseSVN is a reporting tool that can give you statistics about commits to your repository. I ran this report at my day job several times over the years and the output has always been a quiet source of pride for me. I decided to publish the report here since I'm moving on to a new project. If nothing else, it serves as a reminder to myself of what I spent in the last four years doing:
This is the commit history for the entire four years of the project up to the date when this post was published (names have been removed to protect the innocent). I account for about 18% of the total commits to the repository. One of my teammates (who incidentally owns the third highest bar in the graph) claimed that I average 7.5 commits per hour assuming a 40 hour work week (ha). I think his math might be a little off since I calculated a significantly less impressive 5 commits per day when I ran the numbers... although maybe his numbers are a little closer if you exclude the year I served in management.
Hold on a second...
This can't be fair, can it? I've been on the team longer than anyone else at this point, so of course I've amassed more commits. Well, let's look at the commit history for one month during which the team membership was relatively stable:
Me again, and by a wider margin, too.
A lot of my teammates were shocked as these graphs made their way around the team, but there are benefits to working at this pace:
For me, writing software is all about momentum. I am more stressed out at the end of the day if I don't commit a whole bunch of code.
Happy committing!
This is the commit history for the entire four years of the project up to the date when this post was published (names have been removed to protect the innocent). I account for about 18% of the total commits to the repository. One of my teammates (who incidentally owns the third highest bar in the graph) claimed that I average 7.5 commits per hour assuming a 40 hour work week (ha). I think his math might be a little off since I calculated a significantly less impressive 5 commits per day when I ran the numbers... although maybe his numbers are a little closer if you exclude the year I served in management.
Hold on a second...
This can't be fair, can it? I've been on the team longer than anyone else at this point, so of course I've amassed more commits. Well, let's look at the commit history for one month during which the team membership was relatively stable:
Me again, and by a wider margin, too.
A lot of my teammates were shocked as these graphs made their way around the team, but there are benefits to working at this pace:
- You are less likely to break something with a lot of small commits than you are with a few huge ones,
- Your teammates see your code sooner, so you get feedback sooner and reduce the risk of merge conflicts,
- Customers get features and bug fixes sooner (as long as other organizational factors don't stand in the way),
- And plenty of other reasons smarter people have already enumerated.
For me, writing software is all about momentum. I am more stressed out at the end of the day if I don't commit a whole bunch of code.
Happy committing!
Tuesday, November 15, 2011
Octophile: Social media widgets for GitHub
Last week I put one of those Twitter follow buttons on my blog. It was cool, but it looked a little lonely all by itself. It occurred to me that other developers can follow me on GitHub too, so I started looking for a similar button for GitHub. Much to my surprise, I was not able to find one. Luckily, GitHub has a pretty awesome API, so I decided to try to make a follow button for GitHub myself. I cobbled together a small sinatra application, pushed it to heroku, and octophile.com was born!
You can see the GitHub follow button in action off to the right (if you are reading this on my website and not a RSS reader). If you find this button useful, or would like to see some other widgets like this, I'd love to hear your feedback.
Happy coding!
Update 11/19/2011:
I have since found that something like this does already exist: github anywhere.
You can see the GitHub follow button in action off to the right (if you are reading this on my website and not a RSS reader). If you find this button useful, or would like to see some other widgets like this, I'd love to hear your feedback.
Happy coding!
Update 11/19/2011:
I have since found that something like this does already exist: github anywhere.
Subscribe to:
Posts (Atom)