Showing posts with label software. Show all posts
Showing posts with label software. Show all posts

Tuesday, October 5, 2021

Things a computer science degree didn't teach me about software engineering

Agent K and James Edwards III, soon to become Agent J, in an elevator. Scene from movie: Men in Black
"Whatever you say, Slick, but I need to tell you something about all your skillz..."

While I'm very happy with / grateful for my computer science degree, I eventually became a software engineer. And the thing about software engineering is that it's related to, but quite distinct from, computer science. Following up on my previous post about that topic, here's the topics and skills I wasn't taught in my computer science undergrad that proved important in my career so far. Along with each one, I'll try and provide the closest thing I remember from undergrad.

Reading other people's code

Almost no professional software engineering is done alone, which means reading other people's code is basically a necessity. And other people's code can be atrocious. While I learned to write well enough to be understood, the skill of fitting my mind around someone else's mental model to understand their code was rarely exercised.

Incidentally, one study has suggested that aptitude to learning other programming languages is related to aptitude in learning natural languages significantly more than aptitude in the mathematics that makes up the core of computer science.

Closest I got: We did some debugging exercises which touched on this space, but the biggest match was becoming a teaching assistant (TA) for one of our courses involving a functional programming language gave me a lot of practical experience in understanding other people's code. I recommend it highly.

Choosing what problems to solve

It turns out, there are infinite problems to be solved with computers, and succeeding in a software engineering career involves a certain "nose" for which ones are the most valuable. Growing this skill can involve both introspection and retrospection, and is generally not a challenge that comes with undergrad, where problems will be handed to you until you complete coursework. Any software engineering career track will eventually expect you, the engineer, to generate the problems that should be solved; those are the engineers that a company needs, because they underwrite the bottom line for the next five years.

Closest I got: I undertook an independent study, but unfortunately it put me off of grad school and PhD pursuit. The gaping maw of the unknown was too much to stare at. But over time, I've been able to learn this skill better on-the-job.

Deciding on a solution, designing it, and defending it

With coursework, there is generally one metric by which your solution is evaluated: passing grade. But engineered software will be maintained by other people and often interlocks with a huge edifice of already-existing technology operating closer to or further away from the end-user. The right way to solve these problems is your responsibility to create, and your design will often require explanation or even defense of its fit vs. alternatives. These aren't software skills; they're people skills and they involve understanding not just the system you're building but the people interested in seeing it built, as well as time and cost tradeoffs.

Closest I got: As a TA, I had an opportunity to design new lessons and put them in front of the professor. This was an excellent opportunity to refine my ability to explain and defend something novel I'd created.

Huge systems

Corporate software accretes features. Once something is built, it is incredibly rare for the company to decide to stop doing whatever the thing is that the software supports.

As a result, becoming a member of a large team at a mature company will often mean struggling with software of absolutely enormous complexity. This isn't useful to do to undergrads; pedagogy is better served by approaching the elephant one piece at a time, and any support systems needed for a class tend to be slim and focused. But big-world software (especially web software) is not designed to be understood by one person; that's why companies hire teams, and it's why reading other people's code becomes so important. A huge system requires some limited expertise in a broad skillset, because eventually an abstraction breaks and, for example, you have to care about the network layer to fix the UI, or the 3D rendering layer to build the schema for a new dataset, or the package management layer to write any code at all. And industry software often has to make practical tradeoffs that more abstract, academic software can skip... Does your system have a time limit in which it can operate, because you're building banking software and your peers turn off on weekends? Is your website breaking because you've hit the limit for max URL size (which is implementation-dependent)? Are you building on a system that lacks a floating-point coprocessor (or, for that matter, a "divide" operator)?

Closest I got: Reading the source code and design notes of big systems. The Linux kernel is open-source. So is Docker. So are multiple implementations of MapReduce. Reading and really trying to understand all parts of one of those elephants can give a person perspective on how wide the problem space can be.

Handling user feedback and debugging user bug reports

Real-world systems have real-world users, and users are the greatest way of finding faults that have ever been devised. Software of sufficient quality to pass academic exercises is often not nearly well-tested enough to be easily maintained or improved in the future, or grow to handle new requirements; it's write-and-forget software. Related to this: academic projects that require code often provide relatively straightforward feedback on problems. User feedback is anything but straightforward (when it isn't outright abusive, because users are emotionally angry that your tool got them 99% of the way to where they wanted to be and then ate their data).

Closest I got: While I actually had a few classes that taught expecting the unexpected, nothing was as educational as building real software that real users use. Whether it's maintaining a system relied upon by other students or building a library used by people on GitHub, getting early experience with the bug-debug loop with strangers in that loop is useful.

Instrumentation

More challenging for a software engineer than vague or angry bug reports is the bug report that never comes, where the user just gets frustrated and stops using your software. Software operating at huge scale can't rely on users to have the time or patience to file reports and must police itself. The practice of instrumenting systems (encompassing both robust error capture and reporting and observation of users' interactions with the tools, what pieces they care about vs. what goes unused, whether they often get halfway through a designed flow and stop, etc.) is as much art as science, but it demands its own attention to design and implementation.

Closest I got: This is a big piece that was basically never touched on in undergrad. Debugging exercises were close in that they could involve temporary instrumentation to understand code flow, but this is a skill that I think may be under-served (unless pedagogy has changed in fifteen-odd years).

Automating toil and transforming big blobs of text

While code isn't text, much code is represented as text and mechanically, a software engineer spends a lot of time moving text around. A significant difference between a stagnant software engineer and a growing one is whether they get better at that process. That can involve both practice and recognizing when a particular task is a "hotspot" that is repetitive drudgery enough that it could itself be automated. There's no rule against writing code to write code; half of software is probably exactly that. I like to rely on tools that make this kind of growth easier, and I'll touch on that in a later blog post.

Closest I got: Computer science exercises tend to try and avoid being the sort of thing that can be solved with moving big blobs of text, trying to instead focus on problems solvable with the right (relatively) few lines in the right place. One exception is an operating system design course I took, and that one gave the most incentive to learn how to quickly replace phrases globally, or move an entire data structure from one form to another (when we the developers didn't expect that change so it wasn't cleanly structured to do it).

Growing your tools

Related to the previous topic: the industry will change over time, and the tools available to you will change with it. Like any good practicing laborer in a technical field, be it medicine, agriculture, or manufacturing, learning of new tools and improving the ones you already have is a continual exercise. The ride doesn't end until you get off.

Closest I got: Everything and nothing. Improving and seeking to improve one's tools has to become like breathing air to a software engineer; this is the "piano, not physics" part of the practice of programming itself. Better IDEs, better languages, best practices, new frameworks, new ideas... Seek them out, and may they stay exciting to you your entire life.

Tuesday, September 28, 2021

Things I wish I'd Known Before Getting A Computer Science Degree

 Hi, my name is Mark, and I struggled with impostor syndrome for decades.

("Hi, Mark").

In my case at least, impostor syndrome came from the discontinuity between being good at things in high school and far more mediocre in college. I came from a place where I thought I had things figured out to a far more challenging environment in a different city, with peers who knew way more than I did about a subject I thought I was passionate about. For awhile, I seriously considered changing disciplines.

These feelings don't really chase me anymore, but I've thought for some time about why they came up and what I wish I'd known—in the high-level sense, not the day-to-day sense—about the path I'd started out on.

So, dear Mark in 2001, here's some things Mark in 2021 gets to know.

Computer science is more math than engineering

You're into computers because of what they can do. That's cool. Computer science is about why they do it. And the why brushes up against the limits of what they can do. You've never thought too much about that aspect of things. You'll be thinking about it a lot these next four years. Some of this will be hard, because you have a mind that thrives on learning things you can apply right now, not on abstraction and theory. 

You'll adapt. 

It'll hurt like hell. 

One thing that helps adaptation: tie the whys to the whats. Learn the history of how we came to know these things (oftentimes, we learned them because someone was chasing a what, got stuck, and had to chase a why to understand the reasons "what" wasn't working). Computer science as a pedagogy isn't interested in its history, so it won't hand those pieces to you... But you are. Seek them out.

Software engineering isn't computer science


You're gonna be real discouraged in freshman year because a full half of your classes are deep math you haven't seen before. That's okay. It gets good junior and senior year when you start applying the theory on larger projects. But understand: that theory is computer science. Your peers that go on to grad school or to pursue a PhD, they aren't likely to be designing the next generation of developer tooling—they might, but they can also make a career out of never publishing any mainstream-known software. This discipline is about understanding the pure-math space of computation as much as the machines, "meat and bones" of how to make a computer go.

Computer science isn't software engineering


On the flip side, there are things you will absolutely need to know to be a successful writer of software people use and maintain that a computer science undergraduate degree doesn't need to teach you. I've read some brilliant code by some very smart researchers that is incredibly bad for communicating the concepts behind the code to other people—single-letter variable names, zero comments, no encapsulation, and so forth. It's not necessary to do those things to publish novel research, so those things aren't done. I'll write another article on this topic in the future.

Computer science relies on mathematics not always taught in high school

Palpatine talking to Anakin Skywalker from Star Wars. "Have you heard the tale of the Travelling Salesman? It is not a story the calculus teachers would tell you."
Professor Palpatine introducing a freshman to discrete mathematics

Computer science is the science of algorithmic solution of problems: what problems can be solved by algorithm, how hard it is, and what algorithms are the same thing in disguise. Of the mathematics an average US high school student will know, the least likely in this set is discrete mathematics. Discrete has few practical applications relative to algebra or calculus, so it often falls by the wayside. But it's fundamental to computer science, because it deals with the mathematics of processes that progress in orderly, distinct steps.

You don't really know the term "discrete math" right now. You'll be comfortable enough with it to make jokes about it soon.

If there were a way to bootstrap yourself into this before getting to college—summer class, elective in high school—I can't recommend it enough. They'll teach it to you in college, but if you get there knowing it, it'll be one fewer thing to worry about while you're trying to stuff everything else into your brain.

The industry is young, and pedagogy varies wildly


Your father made a career out of writing software on the back of an English undergraduate degree. As a disciplined field, computer science is young; pedagogy varies wildly between institutions that offer computer science degrees. You'll take an internship in Philadelphia where you'll meet several students from Drexel; they'll run circles around you in terms of prolific code authorship, because they've been using the tools the company uses since freshman year. But Carnegie Mellon intends to graduate potential grad students and PhD candidates from their undergrad program; they start computer science on fundamentals of theory and mathematics, and most code can be written in a plaintext editor. Drexel, to my observation, biases towards getting students productive earlier; they have classes intent on teaching more intricate and esoteric parts of tools like Visual Studio (whereas CMU's opinion on such things is that Visual Studio didn't exist 25 years ago and may not be in use 25 years from now). So you'll be slogging through "How to set up a project" while they're scripting the debugger. Tuck in.

The field is moving so fast, it's impossible to predict what will be relevant

When you start, you'll meet some grad students who are very excited about computer simulation of electronic circuits. The idea they're chasing is that instead of building a circuit by exhaustively laying out logic gates on a chip, you instead use a high-level language to describe what the chip has to do, and an algorithm could "grow" a chip's physical design by heuristically selecting logic gate configurations to satisfy the requested input. This process will become standard in huge swaths of hardware design for new custom circuit fabrication. You'll also watch sloppy prototypes of voice recognition and image recognition explode into bog-standard technologies that come on every smartphone as a pack-in deal.

Faced with this rapid pace, the only option is to tuck in and prepare to be learning something new continuously, your whole career, forever. As one of your mentors freshman year will say, "Programming is piano; it's not physics." This is a field where what is possible is expanding at a rate we can barely keep up with. Enjoy the ride; don't get lazy.

Set aside time to get good at the tools

Corollary to the previous: a lot of classes will focus on teaching theory and rely on the students to learn the mechanics of their tools on their own. If it can be read out of a manual, it's not something the college is focused on expositing (the manual is, after all, right there).

Set aside time to get good at using your tools of choice and exploring their nooks and crannies. The biggest favor you'll do yourself in your undergrad is taking the time one summer to learn the esoteric language used in the class you'll be taking next semester. It will still be a challenge, but you won't be struggling over syntax and theory at the same time.

Put thought into where you want to be in four years

College can give you tools to succeed but won't tell you who to be. It can be easy to get so heads-down on the tasks and classes that you wake up one day and realize you don't have a life path beyond "student." That can be your life path, but if you want that, seek out grad students and PhD candidates and talk to them. You might find what they do with their time is what you want to do; you might not! Research and deep theory isn't for everyone.

If, instead, you choose industry, you will never be freer in your life to choose which one you pursue than graduation year. You will have an incredibly valuable talent to share with the world; it's up to you who benefits from that. Make sure it's someone who deserves it.


Whatever you're interested in doing, computer science will get you closer to it

Software is eating the world. There are very few things humans do now—in industry, in recreation, in travel, in finance—that don't (or can't) involve a computer somehow. And the addition of computers opens entirely new ways of doing things that weren't even considerable previously. The skills you will acquire to understand the fundamentals underpinning all of these new approaches and technologies will be invaluable navigating the world that is evolving in front of us.

This is going to be a hell of a good time. Enjoy it.

Monday, July 13, 2015

Cecilia's Drawing Toy: Updated for Little Hands

You learn a lot about proper software design by actually watching users. This is a well-known fact, but sometimes a reminder is helpful.

Cecilia was in town for several weeks, so I got a chance to watch her playing with her drawing toy. I noticed that she preferred to hold the phone tightly with one thumb, which really threw off the motion tracking.

A few changes later, I added support for multiple-touch operations. If little hands need to grab the screen in two places, that's not a problem!

It's also good for finger-painting. :)


As always, the updated version is available on the Play Store if you're not in the mood to mess around with compiling your own.

Sunday, June 29, 2014

Cecilia's Drawing Toy

Image from the game: a smiling sun on the right, and a set of color markers on the left.
My sister- and brother-in-law recently celebrated the birthday of their second daughter, Cecilia. She's not quite old enough to care about the touchscreen devices yet, but I figured I'd get a head-start on this one. So here you go: Cecilia's Drawing Toy.

Much like its "sister" software, Frances's Tracing Game, this one is available for download on the Play Store, or you can poke through the source code on GitHub. You can select a color and draw with your finger, then to erase, hold the whole device upside-down and shake it (like another drawing toy you might have heard of ;) ).


I was able to re-use quite a bit of the source code from Frances's Tracing Game for this one, so the build cycle wasn't quite as long. I feel like I'm pretty much getting the hang of the drawing toolkit for Android; this one should play more nicely than the first versions of Frances's Tracing Game did with different screen sizes and form factors. It's missing a way to save images right now (but you can do so with the "screenshot" feature on most phones).

If you thumb through the source code, you'll find some goodies. It's all licensed under Apache, so feel free to grab any pieces and use them in your own projects (with proper sub-licensing and attribution). In particular, these modules are broken out and intended for re-use:


  • RandomSound: Give it a collection of sound resources and it will select, load, and play random ones continuously. Useful for footsteps, etc. There's some lag between the end of one and the beginning of the next that I should clean up.
  • OscillationSensor: Detects back-and-forth jerks in a linear direction. Useful for detecting device shaking.
  • FaceDownSensor: Detects when the device's screen is angled towards the floor, and sets a simple flag that the render thread can check.
As always, I dug up some likes and dislikes as I put this together.

Things I Liked

  1. Code re-use worked as intended. :) Both the initial layout of the project and the push to the Play Store were a lot simpler with templates ready to go.
  2. Instead of wrestling with Windows to build this one, I put together a development environment in a virtual machine running Debian Linux (with my favorite tool suite---emacs, git, and the command-line SDK---installed from packages and ready to go). It turned out to be a smoother development environment, to my mind; I spent less time thrashing about with the command line and more time writing code.
  3. Build-test cycle on a connected Android device is super-simple. I just kept "adb logcat" running in one console to spot-check installation failures, had "ant debug && ant installd" as my compilation command, and off I went.

Issues

  1. The timestamp on accelerometer events is extremely wonky. As I discussed previously, I eventually ignored them and tagged every incoming sensor event with a datetime as the sensor thread got ahold of it; not accurate, but sufficient for OscillationSensor.
  2. The Play Store makes you (hypothetically) put together a lot of images and material for a proper launch. Screenshots for phone, for tablet, for 10" tablet, promo image, icon, big super-awesome icon if you end up in the "Featured" section... It'd be cool if at least some of that could be dynamically generated. I spent more time preparing and uploading PNGs than I did writing RandomSound.

Future Ideas

This'll be a fun one to keep updating, but really, the list of new things is small on this one:

  • save the current image when the app quits or is killed
  • allow for mixing of your own marker colors
  • eraser (besides holding upside down and shaking)
  • undo

Enjoy!

If you have a little one in your life, feel free to grab the toy and let me know if they like it! Feedback is welcome on the game's Play Store page, here, or on Plus. Go make something cute. ;)



Thursday, September 26, 2013

Frances's Tracing Game v1.04: On Coordinate Transforms

As one bug dies, another is born. So it goes, so it goes. :)

As  I prepared to publish version 1.03 of Frances's Tracing Game, I took a glance at the statistics and suggestions from the Google Play Store and noticed that many newer Androids couldn't pick up the latest build because I had the minimum SDK version set to 1. I forked the project (Play Store lets you publish multiple binaries under the same app depending on the version of the client's phone), then in the fork I bumped the minimum up to SDK 4 and targeted SDK 11.

That introduced a new oddity: Apparently, my game had been running in a compatibility mode this entire time (due to its claim that it targeted SDK 1); as a result, the screen had been scaled automatically to fit the ancient Android form factor seen in the days of the Nexus 1. When I bumped to SDK4, the compatibility mode went away and my shapes were suddenly far too small to be traced by even clever fingers (screen resolution has improved a lot since the Nexus One days). The Android screen format rules have an awful lot of complexity to them, but to make a long story short: it is best to assume that your client's device could fit just about any rectangular form factor, much like we do with desktop PC programming.

Fortunately, this is an old problem with some old solutions. Many graphics libraries (such as OpenGL) provide a layer of transformations to make it easy to convert from one coordinate space to another. Android's graphics SDK is no exception; the Canvas object supports scaling and translation methods to tweak the underlying matrix that maps pixels in the canvas to pixels on the screen. I'd already used translation previously to center the image; now I just need to use scaling to balloon the image to fit the width of the screen.

Here's the basics of the tweaks I made; if you want to see the full code, it's hosted on GitHub.

Step 1: Tweak the scale

Tweaking the scale is extremely straightforward in Android; there's a pair of scale() methods that wrap the matrix multiplication logic for convenience (note: if you haven't done graphics programming, matrix algebra is basically the bread and butter of shifting coordinate spaces; Wikipedia can get you started on the concepts). I'm using this method, which includes the concept of a pivot point (the point around which the scaling should occur) to scale from the center of my image.

The only interesting part is determining how much to scale. I want to take up about 80% of the space, so I'm solving for S in the equations
final_image_width = 80% * screen_width
final_image_width = S * initial_image_width

This resolves to S = 80% * screen_width / initial_image_width, which I store and use as the scaling factor to rescale the canvas. Since the canvas itself is scaled, the image, line thickness I use for drawing, and pink tracing overlays are all correctly rendered without having to change the rest of the drawing logic.

Step 2: Unscale the touch

The only remaining issue is that the touch events we receive don't go through the rendering logic to be scaled. So for touch events we get from the screen, we need to reverse the scaling operation to get them back into the image coordinate space to determine if they were close to one of the tracing lines. I could have done this by grabbing the canvas's matrix with getMatrix() and inverting it (for a given transformation matrix, inverting the matrix gives you the reverse transformation). But I got a little bit fancy instead; the steps to reverse the scaling of the point (factoring in the pivot) are basically as follows:
  1. subtract the pivot coordinates from the point coordinates (this would translate a point on the pivot to (0,0), which is what you want because that point doesn't move under scaling).
  2. Multiply the point coordinates by 1/scale_factor.
  3. Add the pivot coordinates back to the point coordinates (reversing the translation).
It's mathematically simple enough that I think it's clearer to just represent it directly without the matrix (though just using the matrix inverse might very well have been less code!).

The Result


Image from the game: A smiling face, with the word "Smile" beneath it.
I am not unhappy. :)

I'm pretty happy with the result. Frances's game looks good on a 7-inch tablet and on a smartphone. I also added a text label for each picture, because she's getting older and is starting to read and write. I passed the labels through the canvas transformation, which leads to some odd scaling that's dependent upon the underlying size of the traced image; I might make some time to clean that up later. 

Feel free to download it to your own device if you'd like; it's free and will never include ads (because what possible use could ads be in a toddler's tracing game other than to junk-up the advertiser's signal with unintended clicks?).

I have no idea if this simple game will hold her interest much longer; it may soon be time to make her something new. And there's still the question of what to make for Cecilia, but there's a bit of time there; she's not very interested in tablets right now because she just realized that when she kicks her legs, her bounce-chair moves. I have to admit, that's pretty cool.

Monday, May 27, 2013

Frances's Tracing Game v1.03

I love three-day weekends. They give you time to think!

I've dusted off Frances's tracing game and finally fixed a niggling bug that had been bothering me. I'd taken a shortcut on centering the images on the screen by putting the image in a view that was embedded within a larger view. That solution had been playing passably, but it had annoying side-effects in Jellybean:
  1. Touch-drags that started outside the image didn't register when you dragged inward.
  2. As the drag operation happened, an annoying grey rectangle would flicker around the frame of the inner view. I never figued out the root cause of that.
I therefore ditched the two-view solution and did the math internally to set up the images in the center of the view (there's a convenient Canvas.translate method to do the offsetting for me in terms of drawing, and I do the translation of touch events from view coordinates to image coordinates myself).

There's a lesson in this. Sometimes, an operating system changes and it becomes clear that the OS's developers don't have your use case in mind. When that happens, there are a couple of ways you can respond. But when you're working on a hobby project and don't have the clout to steer the OS project itself, it's often easier to adapt to the changes than it is to push back. Don't be afraid to replace someone else's functionality with your own code. But when you can contribute, do; the svg-android renderer instance that I use seems to have issue with some flavors of transform logic, and if I don't find that this has been fixed in later versions, I plan to patch it and submit the changes back to the maintainers.

The new version of Frances's Tracing Game should be available in the app store some time today.

On a related note, there's one more bug to fix... I'm going to have to figure out what to do about the title. Frances herself is growing out of this game, but her younger sister was born this week. Welcome to the world, Cecilia!

Tuesday, August 7, 2012

Update to Frances's Tracing Game

Frances got a chance this weekend to play with her tracing game some more. She uncovered a bug I hadn't noticed in testing, and I noticed some trouble she was having. So I pushed an update.

Frame bug fix

A bit of inside-ball: under the hood, the traceable image is a custom View embedded in a LinearLayout, with the "gravity" on the LinearLayout set to center the embedded view horizontally and vertically. If Frances starts a trace by putting her finger down outside of the embedded view (i.e. inside the LinearLayout), the Android event system doesn't pass the event to the embedded view, so the trace doesn't get detected even as she moves her finger over the figure. This is perfectly understandable, correct by the UI standards, and 100% wrong for this application. I had to fish around a bit (there are a couple of options that look like they fix this issue and don't quite fit), but in the end the solution was to set the embedded view as the touch listener for the LinearLayout (i.e. embedded view implements OnTouchListener) and translate the coordinates from the LinearLayout's reference to the embedded view's reference using MotionEvent.offsetLocation.

Detect All Points

For simplicity, I'd been using only the first touch coordinate (the output of getX() and getY()) as the tracing location. However, watching Frances play the game, I noticed something about the way she holds the phone: rather than cradling the back, she usually grasps it from the side, which leaves her palm resting on the screen. Naturally, his plays havoc with the touch UI. Fortunately, the fix was simpler than the frame bug: I just read all the touch coordinates and try them all. If you put your palm on the image, that might count as a trace; I'm sort of fine with that, really.

Final Thought: On Users

Watching my niece play this game has been a great motivator for me to fix up the quirks. It's one thing to know you have thousands of anonymous users somewhere out in the world; it's quite another to have a two-year-old sitting in front of you straining her mind to try and understand a concept-breaking bug in the behavior of the program. The frame bug was driving her crazy, but she didn't want her uncle to do it for her; she was insistent on figuring it out herself. If she's going to be a trooper about it, the least I can do is clean up the last 2% of bugs in the toy.

By the time you read this, the latest version should be available on the Play Store. I didn't want to release just a bug update, so there's a couple of new figures in there too. Have fun!

Sunday, July 22, 2012

Frances's Tracing Game (or: How I Spent My Summer Vacation)

A black outline of a house with pink outlines indicating where tracing has occurred.
About the time my niece was first learning to walk, I showed her pictures of our dog on my Android. Within a few weeks, she was asking for the phone every time I stopped by. It was fascinating to watch how quickly she learned the basic controls, such as how to go back and forth in the photo roll and play movies.

I went fishing for some age-appropriate games to put on the phone, and found a couple. One of her favorites is Doodle Toy, a delightful little drawing game. She really enjoyed that, but at some point it went ad supported (not great in a toddler app).

Inspired by the time I taught her to draw circles in Doodle Toy, I played around for a few weeks in a very lackadaisical fashion and put together my first real Android App. It's a very simple tracing game; glide your finger over the black stencil to turn it pink. A little congratulatory sound cue plays when you trace the whole image, and the next one appears. There are only six images; no randomized order, and they loop forever.

Now that the project is done, I have some time to decompress and take stock. Here's a brief tour through the process: What I used, what went smooth, what got bumpy, and what I'd like to do in the future.

The Build Environment

Configuration of an Android project was pretty simple. I started at developer.android.com and basically followed the steps, the key ones being installation of the Android development environment and ant1.8. My primary machine was a Windows Vista box, but for the finishing touches I used a small laptop running Ubuntu Lucid Lynx 10.04. The Ubuntu box was actually a little quicker to set up (with the one exception of the need to switch in ant1.8 instead of the regular ant package; no need to build from source, just a different package). 

In lieu of using Eclipse, I opted for the command line tools and emacs. What I lost in autocompletion and refactoring support I'm pretty sure I made up for in portability, small footprint (I dread the notion of running Eclipse on my tiny laptop), and nearly-nonexistant garbage collection cycles in my IDE. 

The only other issue I ran into was a gotcha that multiple people have reported about the tool suite in Ubuntu: the first time you "ant installd," you're likely to get a permissions error because adb running as a user probably can't access the devices it needs to talk to your Android. The easiest (maybe not so secure ;) ) workaround is to sudo killall adb, then sudo adb devices to force the daemon to run (and stay running) in root. A maybe more secure and less hackish option is given here, but I haven't tried it.

Writing the App

The app is a bit of a hackjob, and it shows... Quite literally, as you can browse the source code here. There's a lack of division of interest (Traceview.java is pretty overloaded to do not just rendering and events, but state change from image to image also). I think there's a fine line one can walk between too much modularity and too much complication; the rule of thumb I use these days is to hack a solution in first, then refactor later. You'll also notice a lack of test cases; the project is simple enough that end-to-end testing takes about thirty seconds, so this hasn't been an issue. Topic for another day: I also find unit testing of essentially visual / event apps a real chore on most architectures, and I haven't yet gone looking to see if there's a clean tool for such things on Android.

One thing I'm pretty happy about is standardizing on SVG for the image format. It really freed me up in the test-and-iterate cycle, as I was able to lean on the svg-edit demo to do all my trace data. I'm pretty enamored with SVG for something this simple; the ability to pop back and forth between visual and text representation of the data turned out to be pretty awesome. I also lucked out and found this awesome SVG to Android Picture converter library, which is Apache licensed. I hacked some specific tweaks into it for my needs, but it was already a 95% fit for my project and saved me the drudgery of hooking up an XML parser (which, I'm told, causes the death of a kitten every time it's done).

Apart from those bits, the only fiddly pieces were the code that senses touch drags and applies them to the image. I originally put a lot of effort into making only the endpoints of a tracing "hot," so you had to trace contiguously. Observation of my niece playing the game showed that it was not only hard to get right, it was irritating to use and placed unneeded restrictions on the player---after all, she's not angling for a high score, she just wants to turn the image pink, and if she wants to do that by squeegeeing with her finger like she's cleaning a window, maybe I should get out of her way! YAGNI.

(There's a future treatise on "Games vs. Toys" here, but it's a story for another day. Suffice to say, I wish I'd remembered the lesson I learned watching her play when I was naming the project, because I should have called it "Frances's Tracing Toy").

Things I Liked

  • The documentation on Android is very decent. It walked me through most of the fiddly bits and platform-specific pieces. I walked through the "hello world" cat example and went from there. Anything I couldn't immediately glean from the documentation, I was able to find in the blogging community and stackoverflow.com.
  • The test-and-iterate cycle is pretty dang short with my phone plugged in. It's smart about the details, like quitting the app if there's already an instance running.
  • The resource abstraction is a pretty good abstraction, and the way resources bind into the code layer is extremely convenient. I ran into a couple of tiny issues where I had behavior in the wrong place in the Traceview code that crashed because it was being run at resource-loading time and tried to touch nonexistant resources, but that was an easy issue to diagnose and solve.
  • Publishing flow to the Play Store was pretty much a breeze. I paid my dues and churned out art assets using svg-edit, Paint.NET, and Gimp. The "promotional" shots I screen-grabbed from the phone itself (Thanks, screenshot hard-button!).
  • One program, compatible with 1,318 device configurations according to the Play Store. Awesome!

Issues

  • The aforementioned device issues on Linux. But that can probably be filed under "Every experience I've ever had plugging a new hardware device into a desktop Linux machine."
  • I tried to get cute by centering my trace object using layouts instead of rolling my own offsetting code for the event and rendering handlers. It ought to work, but it got fidgety; I had to force both a re-render and a re-layout of the containing view as well as the child view (it appears that resizing a child doesn't necessarily trigger a repaint for the child's old position rectangle in the parent, though to be honest I didn't track down the exact details). There's still a squeaky little bug in the interactions between parent and child layers, and I'm sadly increasingly convinced that it hasn't been worth it to try and shortcut writing my own centering code by using the layout engine.
  • I got very weird behavior when I tried to specify the <uses-sdk android:minSdkVersion="15"/> rule in my manifest (as that was the only system I'd tested on): the toy broke utterly (played sound but drew only a white background and nothing more). This was very unexpected behavior, and I haven't yet tracked down a root cause. As a result, I set my minSdkVersion to "1", and I'm hoping I don't get too many bugreports from people who's Android phone was made by cavemen is a bit older.
  • My program is ostensibly compatible with 1,318 device configurations. That's... a lot more than I have access to for testing purposes. I've tested, in fact, on a grand total of one phone. It's a bit freaky to think about all those people who are going to be using this toy on a hardware config for essentially the first time... On the other hand, that's half the fun of PC development, right?

Future Ideas

There's plenty of places I can take this in the future, if I feel like it (and since the code's public, so can you! Just buy me a coke some time if you end up making a mint off of your version ;) )

  • More shapes! My niece's parents have already asked for numbers and letters. I could have fun with that. :)
  • Fading from a sketch outline to a full-color version when you complete a picture.
  • Suppression of the menu bar and soft controls, with a "parent access" secret to get you back to the rest of the phone (personally, I wouldn't recommend letting a toddler have a phone all to herself anyway, but if this offers peace-of-mind to some parents, I could probably make it a thing).
  • Usability tweak: Currently, the event listener does the simplest thing and takes the first-index touch point when detecting a touch. But one thing I've noticed about little users is that they aren't great at balancing the phone yet and tend to grip from the side, meaning there's often a little palm touching the screen providing a false reading. I bet I can work around that if I index through all the touch points. :)

Go Have Fun!

I hope you enjoy the game! If you have any comments, feel free to post them on the game's Play Store page itself. If you have comments for me though, feel free to post them here, or come find me on Plus. I'd love to hear from you!