GeistHaus
log in · sign up

https://coderefinery.wordpress.com/feed

rss
10 posts
Polling state
Status active
Last polled May 19, 2026 02:07 UTC
Next poll May 20, 2026 00:23 UTC
Poll interval 86400s
Last-Modified Mon, 08 Sep 2025 11:27:50 GMT

Posts

Forcing Functions in Software Development
AndroidArchitectureUI Designagilelean startupprogrammingsoftware developmentUX
Here’s an unavoidable fact: the software project you’re working on has some flaws that no one knows about. Not you, your users, nor anyone in your team. These could be anything from faulty assumptions in the UI to leaky abstractions in the architecture or an error-prone release process. Given enough time, these flaws will be … Continue reading Forcing Functions in Software Development
Show full content

Here’s an unavoidable fact: the software project you’re working on has some flaws that no one knows about. Not you, your users, nor anyone in your team. These could be anything from faulty assumptions in the UI to leaky abstractions in the architecture or an error-prone release process.

Given enough time, these flaws will be discovered. But time is money. The sooner you discover them, the cheaper they are to fix. So how do you find out about them sooner?

The good news is that there are some things you can do to force issues up to the surface. You might already be doing some of them.

Here are some examples:

  • Dig out an old or cheap phone and try to run your app on it. Any major performance bottlenecks will suddenly become obvious
  • Pretend you’re a new developer in the team1. Delete the project from your development machine, clone the source code and set it up from scratch. Gaps in the Readme file and outdated setup scripts will soon become obvious
  • Try to add support for a completely different database. Details of your current database that have leaked into your data layer abstractions will soon become obvious
  • Port a few screens from your front-end app to a different platform. For example, write a command-line interface that reuses the business and data layers untouched. “Platform-agnostic” parts of the architecture might soon be shown up as anything-but
  • Start releasing beta versions of your mobile app every week. The painful parts of your monthly release process will start to become less painful
  • Put your software into the hands of a real user without telling them how to use it. Then carefully watch how they actually use it

To borrow a term from interaction design, these are all examples of Forcing Functions. They raise hidden problems up to consciousness in such a way that they are difficult to ignore and therefore likely to be fixed.

Of course, the same is true of having an issue show up in production or during a live demo. The difference is that Forcing Functions are applied voluntarily. It’s less stressful, not to mention cheaper, to find out about problems on your own terms.

If your Android app runs smoothly on this, it’ll run smoothly on anything.

If you imagine your software as something evolving over time, strategically applying forcing functions is a way of accelerating this evolutionary process.

Are there any risks in doing this? A forcing function is like an intensive training environment. And while training is important, it’s not quite the real world (“The Map Is Not the Territory“). Forcing functions typically take one criteria for success and intensify it in order to force an adaptation. Since they focus on one criteria and ignore everything else, there’s a risk of investing too much on optimizing for that one thing at the expense of the bigger picture.

In other words, you don’t want to spend months getting your mobile game to run buttery-smooth on a 7 year old phone only to find out that no one finds the game fun and you’ve run out of money.

Forcing functions are a tool; knowing which of them to apply in your team and how often to apply them is a topic for another time.

However, to give a partial answer: I have a feeling that regular in-person tests with potential customers might be the ultimate forcing function. Why? Not only do they unearth a wealth of unexpected issues like nothing else, they also give you an idea of which other forcing functions you might want to apply. They’re like a “forcing function for forcing functions”.

Or to quote Paul Graham:

The only way to make something customers want is to get a prototype in front of them and refine it based on their reactions.

Paul Graham – How to Start a Startup

If you found this article useful, please drop me a comment or consider sharing it with your friends and colleagues using one of the buttons below – Matt (@kiwiandroiddev)


1 Thanks to Alix for this example. New starters have a way of unearthing problems not only in project setup, but in the architecture, product design and onboarding process at your company, to give a few examples.

Cover Photo by Victor Freitas on Unsplash

victor-freitas-yuv-iwbyvrq-unsplash
coderefinery
http://coderefinery.wordpress.com/?p=360
Extensions
Publishing your node service with DNS-SD/mDNS from an Alpine Linux docker container
Uncategorizedalpinebonjourdockermdnsnoderaspberry-pizeroconf
Multicast DNS service discovery, aka. Zeroconf or Bonjour, is a useful means of making your node app (e.g. multiplayer game or IoT project) easily discoverable to clients on the same local network. The node_mdns module worked out-of-the-box on my Mac. Unfortunately things weren’t as straightforward on a node-alpine docker container running on Raspberry Pi Zero, … Continue reading Publishing your node service with DNS-SD/mDNS from an Alpine Linux docker container
Show full content

Multicast DNS service discovery, aka. Zeroconf or Bonjour, is a useful means of making your node app (e.g. multiplayer game or IoT project) easily discoverable to clients on the same local network.

The node_mdns module worked out-of-the-box on my Mac. Unfortunately things weren’t as straightforward on a node-alpine docker container running on Raspberry Pi Zero, evidenced by this error at runtime:

Error: dns service error: unknown
    at new Browser (/home/app/node_modules/mdns/lib/browser.js:86:10)
    at Object.create [as createBrowser] (/home/app/node_modules/mdns/lib/browser.js:114:10)

Here’s how I managed to solve this. The following was pieced together from a number of sources (linked at the end).

I’ll assume you have a node app using node_mdns to publish your service, and a Dockerfile based on alpine-linux to build your app into an image for running on the Pi.

Firstly, you’ll need to have the alpine packages to run the avahi daemon, along with its development headers and compat support for bonjour. I.e. in your Dockerfile:

FROM arm32v6/node:10-alpine3.9

# Avahi is for DNS-SD broadcasting on the local network; DBUS is how Avahi communicates with clients
RUN apk add python make gcc libc-dev g++ linux-headers dbus avahi avahi-dev avahi-compat-libdns_sd

You’ll need to make sure the DBus and Avahi daemons are started in your container before starting your node app. Since you can only execute a single startup command from your Dockerfile, we’ll need to bundle the commands into a startup script, and run that. In your Dockerfile:

ENTRYPOINT ["./startup.sh"]

And startup.sh:

#!/usr/bin/env sh

dbus-daemon --system
avahi-daemon --no-chroot &
node index.js    # your app script here

Note: --no-chroot is added to avoid this runtime error:

alpine linux netlink.c: send(): Not supported

Build your Docker image (since this is for a Pi Zero in my case, I’m using DockerX to build for the ARMv6 architecture on my Mac. I recommend this over waiting days or weeks for it to build on the Pi Zero):

docker buildx build -t myapp --platform linux/arm/v6 -o type=docker .

Now push then pull your Docker image onto your Raspberry Pi. If you don’t want to use a cloud-hosted registry, I’d recommend taking a look into setting up a local registry to push it directly to the Pi on your local network.

To run your docker image on the Pi, you’ll first need to disable the host OS’s avahi-daemon (if any) to prevent conflicts with the avahi-daemon that will be running inside your alpine-linux container. On Raspbian, you can disable avahi with:

# SSH into your Pi
sudo systemctl disable avahi-daemon

Then to run your docker image:

docker run -d --net=host localhost:5000/myapp

(localhost:5000 here refers to a local docker registry.) Using the host’s network (--net=host) seems to be necessary for mDNS advertisements to function. In theory you should be able to just map port 5353/udp from the container, but this didn’t work. (If you happen to know why, please drop a comment below).

That’s it. If all goes well you should be able to see your service advertised on the local network. E.g. from a Mac on the same network (the last line is our node app’s http service):

$ dns-sd -B _services._dns-sd._udp
Browsing for _services._dns-sd._udp
DATE: ---Fri 29 May 2020---
22:05:29.580  ...STARTING...
Timestamp     A/R    Flags  if Domain               Service Type         Instance Name
22:05:29.582  Add        3  10 .                    _tcp.local.          _hue
22:05:29.582  Add        3  10 .                    _tcp.local.          _hap
22:05:29.582  Add        3  10 .                    _tcp.local.          _workstation
22:05:29.582  Add        3  10 .                    _tcp.local.          _ssh
22:05:29.582  Add        3  10 .                    _tcp.local.          _sftp-ssh
22:05:29.582  Add        2  10 .                    _tcp.local.          _http

Full sample source code on github: https://github.com/kiwiandroiddev/node-alpine-docker-mdns

Credits/References

https://hub.docker.com/r/stanback/alpine-avahi

https://github.com/homebridge/homebridge/issues/613

https://github.com/joyent/smartos-live/issues/669

https://github.com/home-assistant/docker/issues/23

https://stackoverflow.com/questions/30646943/how-to-avahi-browse-from-a-docker-container

coderefinery
http://coderefinery.wordpress.com/?p=329
Extensions
How to Improve Your Tests by Being an Evil Coder
TDDclean codesoftwaretesting
Note: this article assumes you’re somewhat familiar with the idea of Test-Driven Development. Automated tests improve (minimally) the quality of your code by revealing some of its defects. If one of your tests fails, in theory this points to a defect in your code. You make a fix, the test passes, and the quality of … Continue reading How to Improve Your Tests by Being an Evil Coder
Show full content

stained_glass_spiral
Literal example of an upwards spiral. Photo by Alicia-Lee-07, licensed under CC BY-SA 2.0

Note: this article assumes you’re somewhat familiar with the idea of Test-Driven Development.

Automated tests improve (minimally) the quality of your code by revealing some of its defects. If one of your tests fails, in theory this points to a defect in your code. You make a fix, the test passes, and the quality of your software has improved by some small amount as a result.

Another way to think about this is that the tests apply evolutionary selection pressure to your code. Your software needs to continually adapt to the harsh and changing conditions imposed by your test suite. Versions of the code that don’t pass the selection criteria don’t survive (read: make it into production).

There’s something missing from this picture though. So far, the selection pressure only applies in one direction: from the tests onto the production code. What about the tests themselves? Chances are, they have defects of their own, just like any other code. Not to mention the possibility of big gaps in the business requirements they cover. What, if anything, keeps the tests up-to-scratch?

If tests are actually an important tool for maintaining code quality, then this is an important question to get right. Low-quality tests can’t be expected to bring about higher quality software. In order to extract the most value out of automated tests, we need a way to keep them up to a high standard.

What could provide this corrective feedback? You could write tests for your original tests. But this quickly leads to an infinite regress. Now you need tests for those tests, and tests for those tests, and so on, for all eternity.

What if the production code itself could somehow apply selection pressure back onto the tests? What if you could set up an adversarial process, where the tests force the production code to improve and the production code, in turn, forces the tests to improve? This avoids the infinite regress problem.

It turns out this kind of thing is built into the TDD process. Here are the 3 laws of TDD:

  1. You must write a failing test before you write any production code.
  2. You must not write more of a test than is sufficient to fail, or fail to compile.
  3. You must not write more production code than is sufficient to make the currently failing test pass (emphasis mine).

It’s following rule 3 that applies selection pressure back onto the tests. By only writing the bare minimum code in order to make a test pass, you’re forced to write another test to show that your code is actually half-baked. You then write just enough production code in order to address the newly failing test, and so on. It’s a positive feedback loop.

You end up jumping between two roles that are pitted against each other: the laziest developer on the planet and a test engineer who is constantly trying to show the developer up with failing tests.

Another benefit to being lazy is that it produces lean code. At some point, there are no more tests to write; you’ve implemented the complete specification as it’s currently understood. When this happens, you will often find that you’ve written far less code than expected. This is a win because all else being equal, less code is easier to understand.

Reading about this is one thing, but it needs to be tried out to really grasp its benefits. It turns out there is an exercise/game called Evil Coder that was created to practise this part of TDD. You pair up with another developer, with one person writing tests and the other taking the evil coder role:

Evil mute A/B pairing: Pairs are not allowed to talk. One person writes tests. The other person is a “lazy evil” programmer who writes the minimal code to pass the tests (but the code doesn’t need to actually implement the specification).

You can try this out by heading along to the next Global Day of Code Retreat event in your city – they are a lot of fun.

TL;DR: Improve your tests and your production code as a result, by being lazy and evil.


Thanks to Ali and Xiao for proofreading and providing feedback on a draft of this essay.

stained_glass_spiral
coderefinery
http://coderefinery.wordpress.com/?p=288
Extensions
“Business needs vs. Customer needs” is a False Dichotomy
Uncategorizedbusinessmobile developmentstartupsUX
Why "balancing customer needs with business needs" is short-term thinking.
Show full content

med-badr-chemmaoui-ZSPBhokqDMc-unsplash

“We have to balance the customer’s needs with the business needs”.

How many times have you heard this while working in a software development team?

I’ve worked as a mobile developer at a number of large companies. In enterprise environments like these, typically the mobile app is “the storefront of the business”, and brings together a number of features paid for by other departments.

Often the initial requirements from the other department will come with a suggestion to make their feature more prominent in the app. For example, “add it to the top of the dashboard”, “just add a new tab for it” or “send a push notification to our users about it”.

This is understandable. The job of the people from the other department is firstly to improve the area of the business they are responsible for. Their job is not to work out how to nicely integrate their feature into the app so it plays nicely with every other feature. That’s the app team’s job.

When members of the app team point out that adding a new top-level tab or push-notification for every new feature requested by every department isn’t a sustainable long-term strategy, and will lead to a poor user experience, the protest that often comes back is something like:

Well, we have to remember to balance the customer’s needs with the business needs.

I was never comfortable with this statement. It’s taken me a while to think through exactly why this is. What I eventually concluded is that while it seems reasonable on the surface, buried in it is a wrong assumption.

It’s not that you should always prioritize the customer’s needs over business needs, or vice versa. Rather, the assumption underlying the statement – that these two things are at odds – is wrong. It’s a false dichotomy.

To believe that “balancing the user’s needs with business needs” makes sense, you need to be engaged in short-term thinking of one kind or another.

If you want your business to survive in the long term, there can be no distinction between the interests of your customer and those of your business.

Your business exists to serve a customer, in a sustainable way. In the final analysis (assuming a free market where your customers can leave), business needs and customer needs must be aligned. Promoting one at the expense of the other actually harms both.

In the long term, building a system that helps the business at the expense of your customers is actually harming both the business and your customers. (Spamming them with notifications in an attempt to boost engagement, for example).

Likewise, building a system that helps your customers at the expense of the business is actually harming both your customers and the business.

How does this second point make sense? I.e. how is that helping your customers at the expense of the business actually harms them?

Here’s how: presumably, your customers would rather your business continues to exist than not. For example, bribing customers with giveaways and subsidized prices isn’t sustainable. If you “spend 1 dollar to make 80 cents”, you will eventually go out of business.

When this happens, you will (at the very least) inconvenience your customers, leaving them bereft or forced against their wishes to switch to a competitor. Or if you offer something unique, you deprive them of that unique offering altogether.

Is it idealistic or wishful thinking to see the success of your customer and business as inextricably linked? Jeff Bezos, the CEO of Amazon, doesn’t seem to think so. The top 3 of his 4 pillars of Amazon’s success are:

  1. Customer Obsession
  2. Eagerness to Invent to Please the Customer
  3. Long-term Orientation

So next time you hear that the “needs of customer need to be balanced with the needs of the business” remember that to successful businesses, there is really no distinction.


Thanks to Xiao and Arun for their feedback and suggestions. Liked this article? Please consider sharing it with your friends and colleagues with one of the buttons below.

med-badr-chemmaoui-ZSPBhokqDMc-unsplash
coderefinery
http://coderefinery.wordpress.com/?p=271
Extensions
Beyond DRY – Why Redundancy Makes Your Code More Robust and Less Fragile
Uncategorized
Anti-Fragile by Nassim Nicholas Taleb is a goldmine of practical ideas for software developers, despite it not being a software development book. Redundancy is one example of such an idea that is explored. Taleb explains how having some redundancy reduces fragility, and means we don’t need to predict the future so well. Think of food … Continue reading Beyond DRY – Why Redundancy Makes Your Code More Robust and Less Fragile
Show full content

Lungs are example of redundancy in nature

Anti-Fragile by Nassim Nicholas Taleb is a goldmine of practical ideas for software developers, despite it not being a software development book.

Redundancy is one example of such an idea that is explored. Taleb explains how having some redundancy reduces fragility, and means we don’t need to predict the future so well. Think of food stored in your basement, or cash under your mattress.

Taleb notes how nature’s designs frequently employ redundancy (“Nature likes to overinsure itself”):

“Layers of redundancy are the central risk management property of natural systems. We humans have two kidneys […] extra spare parts, and extra capacity in many, many things (say, lungs, neural system, arterial apparatus), while human design tends to be spare and inversely redundant, so to speak – we have a historical track record of engaging in debt, which is the opposite of redundancy”

Software source code is a good example of human design that tends to be “spare” (having no excess fat) and “inversely redundant”. Redundancy in code is traditionally avoided at all costs. In fact, one of the first principles that junior developers are often taught is the DRY principle – Don’t Repeat Yourself. As far as DRY is concerned, redundant code is a blight that should be eliminated wherever it shows up.

There are good reasons for the DRY principle. Duplicate code adds noise to the project, making it harder to understand without adding any obvious value. It makes the project harder to modify because the same code must be maintained separately at each place it is duplicated. Each of these locations is also another opportunity to introduce bugs. Duplicate code feels like waste.

However, as Taleb states:

“Redundancy is ambiguous because it seems like a waste if nothing unusual happens. Except that something unusual happens – usually.” [emphasis added]

What are these “unusual things that usually happen” in software development? And how could duplicate code possibly help protect us against them?

The Wrong Abstraction

Firstly, remember that duplication is eliminated by introducing abstractions, such as a function or class. The problem with abstractions is that it is difficult to know ahead of time whether a chosen abstraction is actually a good fit for your project. And the cost of getting this wrong is high. Poorly-chosen abstractions add friction to making the kinds of changes that are actually needed for the project, while still exacting an ongoing cost in terms of complexity. There’s also the risk that by the time poor abstractions have been recognised as such, they have already spread throughout the project. Rooting them out at this point will likewise impact code all throughout the project, potentially with unintended consequences.

The “unusual things that usually happen” in software development are unexpected, unpredictable (and unavoidable) changes in business requirements. These have the annoying effect of revealing the shortcomings of your abstractions, abstractions that you perhaps added while faithfully following the DRY principle.

Too-eager abstraction and a lack of redundancy mirrors the problems of centralisation, another idea explored in Anti-Fragile. Centralisation, while efficient in the short-term (read: less code), makes systems fragile. When blow-ups happen, they can take down (or at least damage) the entire system. NNT outlines in Anti-Fragile how such fragility and lack of redundancy was the cause of the banking system collapse of 2008.

Redundancy in the form of duplicated code, on the other hand, makes code more robust. It does this by avoiding the worse evil of introducing the wrong abstraction. In this way, it limits the impact of unexpected changes in business requirements. As Sandi Metz states: “Duplication is far cheaper than the wrong abstraction”

The Rule of Three

As it turns out, there is another software development principle (or rule of thumb) which does recognise the risks of poor abstractions, and seeks to mitigate them through some redundancy. It’s called the “Rule of Three”. It states that you should wait until a piece of code appears three times before abstracting it out. (Note that this appears to contradict the DRY principle). This minimises the chances that the abstraction is premature, and increases the chances that it addresses a real, recurring feature of the problem domain that is worth the cost of abstraction.

Introducing an abstraction is in some sense a prediction of the future. Abstractions make a certain class of future changes easier, at the cost of some extra complexity and fragility. They are worth this cost if and only if the types of changes they make easier actually turn out to be reasonably common. Following The Rule of Three means deliberately holding off on making a prediction until more evidence has come in. The assumption built into the Rule of Three is that past changes are the best predictor of future changes.

Back to Nature

Now to return to Taleb’s observation of widespread redundancy in nature’s designs. An interesting implication of this is that despite all of the apparent “waste” involved, evolutionary processes have nonetheless converged onto it as the best strategy for dealing with unpredictability – a permanent feature of the real world (or at least, a better strategy than no redundancy – having one kidney, for instance).

At a high level, our software projects and teams are similar in the sense that they exist in a challenging, competitive environment punctuated by unpredictable changes. If meaningful parallels can be made between complex systems, it’s worth considering the possibility that despite the apparent “waste” involved, some redundancy is likewise the best strategy for dealing with the unpredictability in our environment too.

This is all to say: go forth and fearlessly copy-paste more code 🙂

References and Further Reading

The Wrong Abstraction

Write code that is easy to delete, not easy to extend

Antifragile: Things That Gain from Disorder (Incerto)

10826112473_7fae4f4978_o
coderefinery
http://coderefinery.wordpress.com/?p=255
Extensions
10 Tips for Exploring Foreign Cities
Uncategorizedchinahong kongmacautravel
  Last month I was fortunate enough to spend two weeks traveling around southern China including Hainan, Guangzhou, Macau and Hong Kong. It was an awesome trip; I would particularly recommend stopping by Hong Kong for a few days to check it out if you get the chance. It’s an amazing, vibrant city. At some … Continue reading 10 Tips for Exploring Foreign Cities
Show full content

 

Ruins of St. Pauls, Macau
Ruins of St. Pauls, Macau

Last month I was fortunate enough to spend two weeks traveling around southern China including Hainan, Guangzhou, Macau and Hong Kong. It was an awesome trip; I would particularly recommend stopping by Hong Kong for a few days to check it out if you get the chance. It’s an amazing, vibrant city.

At some point during the trip I started noting down the things I was learning (about travel in general, and travel around cities in particular) into Evernote. Over time the list kept growing. What follows is an edited version of the original list, compiled into a top 10 (in typical web article fashion…)

1. Get the phone number of contacts in foreign country

If you’re meeting friends at the destination airport, make sure you have their mobile number. Just having them on Google Hangouts, WeChat, Facebook messenger or <insert online service here> won’t cut it as you can’t rely on WiFi access at airports. In Shanghai for example, you’ll still need a local mobile number to access the “Free” airport wifi.

Old fashioned and low-tech is sometimes best.

2. Double check that airports of connecting flights match

Cities can have more than one airport, and they may not be close together at all. As a New Zealander, this was surprising to learn…

3. Bring plenty of cash in the local currency

Unlike credit card and bank cards, cash is guaranteed to be accepted everywhere and is a lifesaver in emergencies.

Even if you’re going to a first-world country, don’t assume your card will be widely accepted, even at popular tourist attractions. For instance, you’ll need cash to buy a ticket for the Victoria Peak Tram in Hong Kong.

Another tip: divide your cash up and distribute it amongst your bags. That way if one goes missing, you still have backups. I had three stashes: one in my checked-in luggage, one in my backpack and a small amount in my wallet.

Again, low-tech = good.

4. Pack the night before

It’s easy to be unrealistic about how easy and fast it will be to “throw everything into your bag in the morning”. If checking out of your hotel room in the morning, do all possible packing the night before.

Ruins of St. Pauls, Macau

5. Invest in good walking shoes

When you’re out exploring all day every day, decent shoes will really pay dividends. Conversely, bad shoes and feet that are killing you each day can put a damper on your travel experience!

6. Sort out mobile data for your smartphone

Having internet access on your smartphone is absolutely essential when travelling, if only for Maps/GPS, Google Translate and being able to research other places to see while you’re already out.

With that in mind, set up global roaming with your mobile provider before you leave, or check if SIM cards are freely available at destination country. Some countries require you to be a local resident and/or have identification to get a SIM card (Hong Kong isn’t like this; China is).

Remember to pack the SIM card removal tool for your phone, if applicable.

If going to a country with restricted internet access, you may want to sort out VPN access beforehand so you can still access the online services you’re used to (Facebook, YouTube, etc). Record multiple fallback IP addresses for your VPN provider as it’s hard to know which will be blocked.

7. Always have snacks and water with you

Bring water and lots of snack foods such as energy bars and nuts in your day pack to keep up your energy levels throughout the day. You never know where you might end up while exploring; it might be a long time between proper meals.

8. Find out the off-peak hours of the tourist attractions you want to visit…

…and go then to avoid crowds. Crowds are pretty much guaranteed no matter the time of day at remotely popular attractions but you can avoid the worst of it with careful planning. Again, this was a bit of surprise to someone from a country as small as N.Z. where things are pretty much guaranteed to be quiet on weekdays and mornings.

9. Get a Metro map

This is a must if you’re checking out any city with a decent metro (e.g. Guangzhou, Shanghai and Hong Kong) due to the sheer amount of time you’ll spend using it. A paper map is better (no worries about dead batteries) or download a PDF online onto your phone or tablet.

10. Invest in or borrow a decent camera

As good as phone cameras are these days, there’s still no substitute for a standalone camera.

And finally (bonus tip 11), if I’ve learned one thing about travel so far it’s this: the big-name tourist attractions at any given destination can be pretty overrated. They’re often geared towards foreigners so much so that they shield you from the actual local culture. Some of the most enjoyable experiences I’ve had travelling have been while wandering around exploring, taking it all in and spontaneously discovering things. So don’t just tick all the boxes, get out there and experience the authentic whatever-place-it-is.

coderefinery
Ruins of St. Pauls, Macau
http://coderefinery.wordpress.com/?p=220
Extensions
What percentage of your users use your app daily?
AnalyticsAndroidanalyticsandroidlean startup
Both the Developer Console and Google Analytics can display your app’s active users – the number of users that opened your app at least once on each day. Knowing the number of active users is a good start to getting an idea of user engagement, but the problem with looking at it in isolation is … Continue reading What percentage of your users use your app daily?
Show full content

Both the Developer Console and Google Analytics can display your app’s active users the number of users that opened your app at least once on each day. Knowing the number of active users is a good start to getting an idea of user engagement, but the problem with looking at it in isolation is that it doesn’t give you any idea of how many of your users have your app installed and don’t open it at all each day.

What’s needed is a new metric with more context – the number of active daily users as a percentage of total users. This is a more accurate indicator of the actual value your app is offering your users, and can be used to validate that specific changes to your app are actually making it more useful or enjoyable (in Lean Startup terms, it is more a core metric and less of a vanity metric).

How to measure daily active users as a percentage for your Android app

You will need:

  • an Android app with Google Analytics and a reasonable amount of analytics data
  • Excel, LibreOffice Calc or an equivalent spreadsheet program for plotting graphs

Note: the sample screenshots I’ve included here use data from my recently released RadioDrive app.

  1. Go to Google Play Developer Console, select your app, go to Statistics.
  2. Select Current Installs by User (this accounts for users that have your app installed on more than one of their devices, unlike Current Installs by Device).
  3. Select 1 year for the time range so you get everything.
  4. Click Export to CSV. In the dialog make sure only the Users -> Current checkbox is selected.

devconsole1_crop

Now we want to get hold of data for the number of active users. The Play Developer Console does have this statistic, but unfortunately you can’t currently export the data. Onward to Google Analytics…

  1. Login to Google Analytics, select “All Mobile App Data” for your app.
  2. Click Active Users from your App Overview page.

ga1_crop

  1. Adjust the date range (drop-down box in the top-right corner) if necessary, then click Export > CSV

ga2_crop

  1. The next step is to import and combine both datasets in Excel. Once you have copied both sets of data into the same spreadsheet, you’ll want to sort the Developer Console data by increasing date so it matches the Analytics data. To do this in Calc, box-select all rows for the date and current_user_install columns, then select Data -> Sort -> Sort by ascending date.

merged1_crop

  1. Move active user data so the dates correspond, if necessary…

merged2_crop

  1. Make a new column for percentage (Formula: =(C6/B6)*100). You can delete the Day Index column now as it’s redundant.

merged3_crop

  1. Plot a line graph (date on X axis, percent on Y axis)

merged5_crop

So far so good, we have a graph showing the percentage of active users each day.

But there’s a problem. Say you release an update for your app that is a total flop. Users start to uninstall your app in droves, except for a small segment of your dedicated fans. In this case, the percentage of active users may actually go up, as your botched update eliminates all but your most loyal users.

If you keep an eye on your other statistics such as daily uninstalls and number of active users (as well as monitoring actual user feedback), you would (hopefully) pick up this kind of scenario. However it’d be nice to be able to see this situation occurring in the same graph.

To do this, you can simply plot current user installs or number of active users on the same axes. That way, you’ll know something is up if either of them start trending downward.

Here I’ve plotted current user installs on a secondary Y axis:

merged7_crop

The final graph (after adjusting the percentage scale to prevent overlap):

merged8_crop

(In case you’re wondering, the lack of active user data until the 8th Dec is due to Google Analytics not being in the app until then!)

Extra credit: add a 3 or 5 day moving average trend line to % Active Users to smooth out fluctuations (having a larger sample size helps with this also).

What core metrics do you measure for your app and what tools do you use to measure them?

coderefinery
devconsole1_crop
ga1_crop
ga2_crop
merged1_crop
merged2_crop
merged3_crop
merged5_crop
merged7_crop
merged8_crop
http://coderefinery.wordpress.com/?p=160
Extensions
Google I/O 2013 – Cognitive Science and Design, and how it applies to Android apps
AndroidUI Designandroidcognitive scienceGoogle IOHMIUIUX
This is an excellent talk by Alex Faaborg at Google I/O 2013 about cognitive science principles and how they apply to interface design. Here’s a summary of some of the main points and how they could be used to improve your apps: We can search for objects of the same colour much faster than searching … Continue reading Google I/O 2013 – Cognitive Science and Design, and how it applies to Android apps
Show full content

This is an excellent talk by Alex Faaborg at Google I/O 2013 about cognitive science principles and how they apply to interface design. Here’s a summary of some of the main points and how they could be used to improve your apps:

  • We can search for objects of the same colour much faster than searching for objects of the same shape [18:26]
  • We can scan a group of faces for one we recognise in parallel rather than sequentially. This could be taken advantage of in messaging and address book apps, for example [10:13]
  • Objects in our periphery are recognised much faster than in our frontal field (tiger example in the video). You can put a small notification icon in the corner of the screen away from the user’s focal point and it will still be noticed [6:50]
  • Colour-deficiency: you can get away with using green and red as long as the contrast is significantly different. Best approach is to test your interface with filtering tools to see how it would actually look (e.g. Photoshop) [13:50]
  • Our brains are very good at recognising patterns. It’s not necessary to group objects together in a box, just having whitespace between groups will do [3:24]
  • You’ll recognise a silhouette of an object that just shows its basic geometry faster than you will recognise a more photo-realistic depiction of the object. This principle is used in the Holo icon set [9:10]
  • Notifications/interruptions wipe the contents of our working memory and make us lose the state of “creative flow” if we were in it. Takeaway: use notifications carefully [22:22]
  • “Chunking” optimizes for our working memory. Examples are the groups of digits in credit card and phone numbers. Make sure your interface supports these chunks and ignores user-entered whitespace! [21:17]
  • We make trust decisions quickly and once made they are slow to change, even to the point of us explaining away new information that goes against them. First impressions matter – make sure you have a quality application icon [24:16]
  • You don’t *have* to be consistent with existing interfaces and interaction paradigms when designing your app. Combining innovation with teaching the user (e.g. with a quick example video) can work well. Example: collaborating on documents via email attachments vs. using Google Docs [31:21]
coderefinery
http://coderefinery.wordpress.com/?p=143
Extensions
Android: 9 patching a family of images the easy way
AndroidBASH scriptingLinux9 patchandroidbashimagemagicklinux
9 patch images in Android are great but if you happen to have a family of graphics to convert, it can get pretty tedious. I had a collection of button graphics that needed converting to 9 patches using the same stretchable regions. Rather than do it all by hand with Photoshop or GIMP (and inevitably … Continue reading Android: 9 patching a family of images the easy way
Show full content

9 patch images in Android are great but if you happen to have a family of graphics to convert, it can get pretty tedious. I had a collection of button graphics that needed converting to 9 patches using the same stretchable regions.

Rather than do it all by hand with Photoshop or GIMP (and inevitably need to redo them all again later when something needed changing) I wrote a small BASH script to do it.

To use the script, first use the draw9patch tool to create the 9 patch info for one of your graphics – this will become the template. Once you’re done, go:

./9batch.sh template.9.png button2.png button3.png ...

to copy the 1 pixel border from the template to your remaining graphics and save a .9.png version of each of them.

Note that you’ll need to install ImageMagick to use the 9batch script:

sudo apt-get install imagemagick

Apparently WordPress won’t let me upload the script itself so here’s the source code:

#!/bin/bash

if [ "$#" -lt 2 ]; then
echo "Usage: 9batch.sh template image1 image2 ..." >&2
echo
echo "Applies 9 patch info to a family of images using one image as the template" >&2
echo "Template image should be 2 pixels wider and higher than source images" >&2
exit 1
fi

# 9 patch image to use as template
src=$1

for i in ${@:2}
do
# use sed to change extension from .png to .9.png and assign result to 'out'
out=`echo $i | sed -e 's:\(....\)$:.9\1:'`
composite -gravity center $i $src $out
done
coderefinery
http://coderefinery.wordpress.com/?p=125
Extensions
Android Device Nudge Detection Helper Class
AndroidJavaaccelerometerandroidgooglejavaprogramming
I recently added a feature to StarCraft 2 Build Player to start playing build orders when the users’ phone is nudged. The idea is so you don’t have to waste precious seconds looking down at your phone to tap the “Play” button, instead you can just mindlessly bump your phone on your desk and you’re … Continue reading Android Device Nudge Detection Helper Class
Show full content

I recently added a feature to StarCraft 2 Build Player to start playing build orders when the users’ phone is nudged. The idea is so you don’t have to waste precious seconds looking down at your phone to tap the “Play” button, instead you can just mindlessly bump your phone on your desk and you’re off.

Anyway, it turned out to be pretty easy to factor this into a reusable class so here it is:

package com.kiwiandroiddev.sc2buildassistant;

import java.util.ArrayList;

import android.content.Context;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.os.Handler;

/**
 * Class for reporting when the device's acceleration (excluding gravity) exceeds
 * a certain value. Compatible with all Android versions as it uses Sensor.TYPE_ACCELEROMETER
 * rather than Sensor.TYPE_LINEAR_ACCELERATION.
 * 
 * NudgeDetector objects are initially disabled. To use, implement
 * the NudgeDetectorEventListener interface in your class, then register it
 * to a new NudgeDetector object with registerListener(). Finally, call
 * setEnabled(true) to start detecting device movement. You should add a call
 * to stopDetection() in your Activity's onPause() method to conserve battery
 * life.
 * 
 * @author kiwiandroiddev
 *
 */
public class NudgeDetector implements SensorEventListener {
	
	private ArrayList<NudgeDetectorEventListener> mListeners;
	private Context mContext;
	private SensorManager mSensorManager;
	private Sensor mAccelerometer;
	private boolean mEnabled = false;
	private boolean mCurrentlyDetecting = false;
	private boolean mCurrentlyChecking = false;
	private int mGraceTime = 1000;									// milliseconds
	private int mSampleRate = SensorManager.SENSOR_DELAY_GAME;
	private double mDetectionThreshold = 0.5f;						// ms^-2
	private float[] mGravity = new float[] { 0.0f, 0.0f, 0.0f };
	private float[] mLinearAcceleration = new float[] { 0.0f, 0.0f, 0.0f };
	
	/**
	 * Client activities should implement this interface and register themselves using
	 * registerListener() to be alerted when a nudge has been detected
	 */
	public interface NudgeDetectorEventListener {
		public void onNudgeDetected();
	}
		
	public NudgeDetector(Context context) {
		mContext = context;
		mListeners = new ArrayList<NudgeDetectorEventListener>();
        mSensorManager = (SensorManager) mContext.getSystemService(Context.SENSOR_SERVICE);
        mAccelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
	}
	
	// Accessors follow
	
	public void registerListener(NudgeDetectorEventListener newListener) {
		mListeners.add(newListener);
	}
	
	public void removeListeners() {
		mListeners.clear();
	}
	
	public void setEnabled(boolean enabled) {
		if (!mEnabled && enabled) {
			startDetection();
		} else if (mEnabled && !enabled) {
			stopDetection();
		}
		mEnabled = enabled;		
	}
	
	public boolean isEnabled() {
		return mEnabled;
	}
	
	/**
	 * Returns whether this detector is currently registered with the sensor manager
	 * and is receiving accelerometer readings from the device.
	 */
	public boolean isCurrentlyDetecting() {
		return mCurrentlyDetecting;
	}
	
	/**
	 * Sets the the amount of acceleration needed to trigger a "nudge".
	 * Units are metres per second per second (ms^-2)
	 */
	public void setDetectionThreshold(double threshold) {
		mDetectionThreshold = threshold;
	}
	
	public double getDetectionThreshold() {
		return mDetectionThreshold;
	}
	
	/**
	 * Sets the minimum amount of time between when startDetection() is called
	 * and nudges are actually detected. This should be non-zero to avoid
	 * false positives straight after enabling detection (e.g. at least 500ms)
	 * 
	 * @param milliseconds_delay
	 */
	public void setGraceTime(int milliseconds_delay) {
		mGraceTime = milliseconds_delay;
	}
	
	public int getGraceTime() {
		return mGraceTime;
	}
	
	/**
	 * Sets how often accelerometer readings are received. Affects the accuracy of
	 * nudge detection. A new sample rate won't take effect until stopDetection()
	 * then startDetection() is called.
	 * 
	 * @param rate  must be one of SensorManager.SENSOR_DELAY_UI,
	 * 		SensorManager.SENSOR_DELAY_NORMAL, SensorManager.SENSOR_DELAY_GAME,
	 * 		SensorManager.SENSOR_DELAY_FASTEST
	 */
	public void setSampleRate(int rate) {
		mSampleRate = rate;
	}
	
	public int getSampleRate() {
		return mSampleRate;
	}
	
	/**
	 * Starts listening for device movement
	 * after an initial delay specified by grace time attribute -
	 * change this using setGraceTime().
	 * Client Activities might want to call this in their onResume() method.
	 * 
	 * The actual sensor code uses a moving average to remove the
	 * gravity component from acceleration. This is why readings
	 * are collected and not checked during the grace time
	 */
	public void startDetection() {
		if (mEnabled && !mCurrentlyDetecting) {
			mCurrentlyDetecting = true;
	        mSensorManager.registerListener(this, mAccelerometer, mSampleRate);

			Handler myHandler = new Handler();
			myHandler.postDelayed(new Runnable() {
				@Override
				public void run() {
					if (mEnabled && mCurrentlyDetecting) {
						mCurrentlyChecking = true;
					}
				}
			}, mGraceTime);
		}
	}
	
	/**
	 * Deregisters accelerometer sensor from the sensor manager.
	 * Does nothing if nudge detector is currently disabled.
	 * Client Activities should call this in their onPause() method. 
	 */
	public void stopDetection() {
		if (mEnabled && mCurrentlyDetecting) {
			mSensorManager.unregisterListener(this);
			mCurrentlyDetecting = false;
			mCurrentlyChecking = false;
		}
	}
	
	// SensorEventListener callbacks follow
	
	@Override
	public void onAccuracyChanged(Sensor sensor, int accuracy) {
	}

	@Override
	public void onSensorChanged(SensorEvent event) {
		// alpha is calculated as t / (t + dT)
        // with t, the low-pass filter's time-constant
        // and dT, the event delivery rate

        final float alpha = 0.8f;

        mGravity[0] = alpha * mGravity[0] + (1 - alpha) * event.values[0];
        mGravity[1] = alpha * mGravity[1] + (1 - alpha) * event.values[1];
        mGravity[2] = alpha * mGravity[2] + (1 - alpha) * event.values[2];

        mLinearAcceleration[0] = event.values[0] - mGravity[0];
        mLinearAcceleration[1] = event.values[1] - mGravity[1];
        mLinearAcceleration[2] = event.values[2] - mGravity[2];
        
        // find length of linear acceleration vector
        double scalarAcceleration = mLinearAcceleration[0] * mLinearAcceleration[0]
        		+ mLinearAcceleration[1] * mLinearAcceleration[1]
        		+ mLinearAcceleration[2] * mLinearAcceleration[2];
        scalarAcceleration = Math.sqrt(scalarAcceleration);

        if (mCurrentlyChecking && scalarAcceleration >= mDetectionThreshold) {
        	for (NudgeDetectorEventListener listener : mListeners)
        		listener.onNudgeDetected();
        }
	}
}

The reason I stuck to using Sensor.TYPE_ACCELEROMETER was because I want to support Froyo with my app. If you’re only targeting 2.3 (API level 9) and higher, you could use Sensor.TYPE_LINEAR_ACCELERATION, and simplify this code a fair bit by stripping out the gravity calculation in onSensorChanged(), etc.

Feel free to use this in your projects. Drop me a comment if you spot bugs or have any suggestions.

coderefinery
http://coderefinery.wordpress.com/?p=113
Extensions