Happy Anniversary
Code Poet, Swordsman, Eternal Wanderer
Please feel free to comment
Email: james AT jameshollingshead DOT com
Monday, October 31, 2022
Friday, October 14, 2022
Sunday, October 31, 2021
Thursday, October 14, 2021
Saturday, October 31, 2020
Wednesday, October 14, 2020
Thursday, October 31, 2019
Monday, October 14, 2019
Wednesday, October 31, 2018
Sunday, October 14, 2018
Tuesday, October 31, 2017
Saturday, October 14, 2017
Sunday, September 10, 2017
Should Bad Code Be Illegal?
A junior developer on my team asked me a rather interesting question
recently.
If you were able to make laws, would you make bad code
illegal?
I have a reputation where I work as being rather hard-nosed
about not allowing hard to understand and maintain code into the codebase, so
my answer surprised him.
No, I would not make bad code illegal, and there are very
good reasons for having this opinion.
First, everyone writes bad code before they write good code.
Whether it’s your first time coding in general or just your first time coding
in a new language, with a new framework, etc, you are going to write bad code.
I’m sorry. It’s just a fact of life.
Nobody starts out writing amazing code. Not me, not you, not
Uncle Bob (in fact, Uncle Bob is open about the fact that he was a horrible
developer when he started, just like the rest of us). We all get better by
writing bad code, learning from our mistakes and pain, and, if we’re lucky,
mentors helping us to see where things can be improved.
Second, “bad code” is rather subjective. What standard are
we using for what “bad” code is?
Is it code that costs the business unreasonable amounts of
money due to being full of bugs and difficult to maintain? That’s not a
completely terrible definition because we can tie it to some external
benchmark, but it’s still probably a bad idea since there is a risk/reward
factor of why decisions were made at the time that may not be known at some
later date.
Is it code that Developer X (whether me, Uncle Bob, or Joe
in the cubicle across the hall) would look at and give a laundry list of things
that need to change? For any given code, you can find some developer that would
find flaws in it – whether it is inefficient, or it doesn’t implement their
favorite pattern, or any number of other complaints.
Is it code that, six months from now, you will look back at
and wonder what the heck you were thinking when you did it that way because
there are so many better ways to accomplish what this code does? Here’s a hint.
This should be most of your code. You didn’t know as much 6 months ago as you
do now.
Third, sometimes bad code is necessary.
Yes, you heard me. There are times that it makes sense to
write bad code.
If you are writing a script to perform some one-off function
that you are seriously never going to use again, pretty doesn’t always matter
that much. If writing code for 5 minutes and then throwing it away can save you
hours of work, go for it. I’ve done it before, and I can almost guarantee that
I will do it again.
Sometimes you need to fix a catastrophic flaw in a mission
critical piece of software, because the company is losing millions of dollars
per minute while the flaw exists. In that case, you have to do whatever you can
in order to fix the issue, release the patch, and *then* go back and make it
maintainable.
The same is true if we are in a startup where the money is
limited, time is fairly short (as it is generally a function of money in this
case), and we need to get a Minimum Viable Product out in front of users so we
can start making money. This is another case were “Make it work, then make it
better” can be the correct (and sometimes only) course of action.
We may not like it, but sometimes getting it to work right
now is the right choice. These situations fall under the heading of consciously
taking on technical debt with every intention of paying it back later and this
is a very important business decision. If it’s a matter of “We can release it
now and be able to make payroll” vs “It will take another month to make it
perfect and we’ll be unemployed by then”, well, the choice is fairly clear then
isn’t it?
Thankfully, most of the code we write doesn’t tend to fall
into those categories, so we can make the effort now in order to make the code
clean and maintainable, but we can’t ignore that those possibilities exist.
Making a law that is, on the whole, completely subjective
and capricious is a just plain bad idea. As a general rule, when we write code
that isn’t amazing, at worst we lose time in figuring out what is going on
and/or fixing some bugs. Nobody generally dies from bad code (there are cases
this is not true, but they are not the norm), and we can always make a conscious
effort to improve it once the crisis is averted.
On the whole, yes, we need to strive to write clean,
maintainable, bug free code, but we can’t be completely inflexible. If you want
to make it a crime to write bad code, make the punishment be to learn how to
write better code. I think that’s a fair sentence.
Current mood: Calm
Current music: Midnight Oil – Beds Are Burning
Labels:
Philosophy,
professionalDevelopment,
Programming,
software,
teaching,
work
Monday, October 31, 2016
Friday, October 14, 2016
Wednesday, December 09, 2015
I will miss you, fuzzy buddy
I lost my best friend today.
Since I was 17, my best friend has been
the amazing cat that you see above the post. He's the best cat I've
ever known. He was smart, brave, sweet, and a very good kitty. He was
also very much daddy's guy. He was always there to greet me when I
woke up in the morning and when I got home from work.
We've been through a lot together,
we've taken care of each other, and we've always tried to be there
for each other.
Unfortunately, today he passed away at
the admirable age of 18. The vet was with us in hoping that he could make it much, much longer for a number of reasons, but it seems that that wasn't meant to be. Yesterday, we got word that he wasn't going
to be around for much longer, so we did what we could – we bought
him fried chicken (which he loved, but didn't often get because it
wasn't very good for him), gave him his holiday gifts early and spent
yesterday and today with him, showing him how loved he is. He ate a
large amount of the chicken and enjoyed all of the cuddles.
We were both there for him when he
passed away, petting and cuddling him while telling him what a good
kitty he has always been and how much we love him. He's now having a well deserved rest and
isn't suffering anymore. We laid him to rest next to Karyl's cat,
Simba, who passed away a couple of years ago and whom he adored
basically since he met her.
I will miss him greatly. Karyl and I
both will, to be honest. The house will feel very empty
without him in it.
To share our love for him with the
world, at least a little bit, here are a few links involving him:
I will miss you very much, fuzzy buddy. You will always have a big place in my heart
Current mood: Sad
Current music: Craig Ferguson's series
finale – Bang Your Drum
Saturday, October 31, 2015
Wednesday, October 14, 2015
Sunday, August 30, 2015
TDD - It'll only hurt for a little while ;)
TDD is a very useful tool.
Done well, it offers assurance that your code does what it is
supposed to do, provides a set of baked in regression tests, allows
you to refactor code without worrying that you will break something
in the process and acts as a sort of living design document.
It is not, however, a
silver bullet. If done poorly, it can cause code to become rigid.
This generally happens when people test the implementation details
rather than the behavior. In a language like Python, where there are
no truly private methods, it can be even easier to test
implementation rather than behavior if you aren't cautious. I'll also
say that it can be really difficult to effectively test Android code
because TDD isn't a first class citizen in that ecosystem.
In my opinion, the
possible downsides of TDD are generally far outweighed by the bonuses
– especially since the downsides are usually able to be mitigated
by exercising some self-discipline.
That's not to say that
every piece of code you ever write has to be done with TDD. There are
some small, usually one-liner style programs, that I write which I
don't test drive. If I'm going to be using something once and
tossing it, provided that there isn't really a danger of permanent
damage if the code doesn't do what I want the first time, I may
decide that it isn't worth the effort to set up a testing harness.
Now that we've got my
opinions on the topic out of the way, I have to say that I don't get
the hate that some developers have for TDD. I can understand a
non-technical manager not understanding how writing tests (more code)
isn't a drawback, but people who write software for a living are
another story.
If I had to guess, I'd say
it was probably some combination of fear of the unknown, a previous
bad experience (whether because of lackluster tools, lack of
guidance, or inexperience), or posturing on the part of the developer
(after all, we are often not known for a lack of ego).
There are occasionally
reasons to not test something (few and far between), but as a general
rule, having the tests will only help you now as well as helping
future you when you have to dig back into the code in 6 months. Not
wanting all of the tools that could help you is just crazy.
This whole post was
inspired by a tweet that a friend of mine
re-tweeted (and its associated tweets), so let's take a little time to critique the conversation.
Shocked by the number of jobs proposed to me whose ads list "TDD" as a requirement. Are we really still doing this?
— Bodil Stokke (@bodil) August 30, 2015
Yes. Because it works. I've already
covered that above, so I'm not going to repeat myself.
TDD works really well for a lot of people, and that's great. Others have different ways of thinking about programming. Don't exclude them!
— Bodil Stokke (@bodil) August 30, 2015
If you are working in a shop that
doesn't do test driving (I'd be tempted to ask why not, but just for
the sake of argument), then yes, a person who doesn't do TDD will be
fine. If you are in a shop that does TDD and the person is willing
and able to learn TDD, then consider helping them get up to speed. If
they refuse, then they're probably not a good fit for your
team/company. Sorry, but that's the way it is.
If your team all does TDD, but one
person refuses to do so, you lose a lot of the benefits. The baked in
set of regression tests is no longer complete. The living
documentation is no longer complete. Refactoring becomes trickier.
Bugs can become harder to track down. These are problems.
@makmanalp @jasonpbecker Yes, I think this is a matter of individual neurology. My architectures definitely get _less_ simple with TDD.
— Bodil Stokke (@bodil) August 30, 2015
Then you're probably doing TDD wrong.
In all likelihood, you are testing implementation instead of behavior.
This is actually a pretty common mistake for people who aren't used
to TDD and it takes some experience to get over. Heck, even people
who have been doing it for a while can slip up sometimes. None of us
are perfect. That doesn't change the fact that you're probably using
the tool wrong.
@makmanalp @jasonpbecker If I'm TDDing with unit tests, it helps me a lot. If I'm TDDing with integration tests, I lose the big picture.
— Bodil Stokke (@bodil) August 30, 2015
You don't TDD with Integration Tests,
or at least not only with integration tests. You use Unit Tests to
test functions (given input, check output for example). Integration
Tests are used to test a series of Units (kind of like checking to
see that a bunch of legos you put together actually build a wall).
@jasonpbecker TDD literally gets in the way of my thinking properly about my code in some cases.
— Bodil Stokke (@bodil) August 30, 2015
Then you're looking at it wrong. Each
unit test should be a condition that your function should fulfil. You
only test the criteria that your code is supposed to fulfill. That's
not to say that you can't spike out something you're not sure of
(especially if you're working with a new language), but before that
code actually makes it into your codebase, it needs to be under test,
and that test needs to fail first.
@jasonpbecker In my case, striving to always do TDD would have a negative impact on the quality of my code.
— Bodil Stokke (@bodil) August 30, 2015
I can sort of get behind this for one
very simple reason – some things are just ridiculously difficult
(if not damned near impossible) to test. These cases don't come up
often, but they do occasionally come up. Other than that, test your
code. For everyone's sake.
@MaggieL "You must know what TDD is" is still a bad requirement. And no, "we do TDD here and so will you" is something I get a lot.
— Bodil Stokke (@bodil) August 30, 2015
I disagree. If the entire team (or
company) does TDD, then yes,
it is a valid requirement for the job. If you refuse to use TDD, then
you don't have a place on the team for reasons that I've already
outlined. In fact, I'm glad more places are making it a requirement
(provided that their people write good tests).
The
sad thing is that I hear these sorts of arguments a lot. Thankfully,
however, I seem to be hearing them less lately. I honestly welcome
constructive criticism of practices because it helps people grow. You
need to be prepared to have others respond to that criticism though.
Just
complaining about a methodology you don't like without valid reasons
doesn't help anyone (and that's exactly what that last tweet was).
Current
mood: Calm
Current
music: INXS – The Stairs
Friday, April 24, 2015
TDD in Android Part 1 – Getting Started
I've recently started learning how to
write apps for the Android platform and, since the company I work for
does all of our development in a Test Driven (TDD) fashion, I'm
writing my first app using the same methodologies.
Unfortunately, the Android platform
apparently wasn't designed with TDD in mind. Automated Unit and UI
testing currently seem to be second class citizens because there
isn't a lot of good information on testing in android. There are now
decent tools, but not a lot of documentation on how to use them or
avoid pitfalls.
I think it's time that we work to
improve that shortcoming, so my plan is to walk you through the
process of creating an app from scratch while pointing out the
pitfalls and gotchas that I come across on the way so you will have a
better chance of avoiding them. This entry represents the end result
of my Sprint 0 (setup) phase for the app. It took about a week and a
half of going through online documentation and talking with
co-workers to get to this point. With this, you should be able to do
it in an afternoon.
My App:
Pocket Bartender. This should be a
relatively simple app since it's just going to be a fairly basic CRUD
app. It will take a drink name or ingredient and give you a recipe or
list of recipes for that drink or the drinks that use that
ingredient. I'll also probably add functionality to favorite and hide
drinks.
My Environment:
Macbook Pro
Phone: Samsung Galaxy s5 (sprint)
running Android 5.0 (Lollipop)
Tablet: Samsung Galaxy Tab 4 (sprint)
running Android 4.4.2 (KitKat)
IDE: Android Studio 1.0.1
Emulator (phone): Nexus 5 API 21 x86
Environment Setup
IDE:
Before you start, you'll need to have
the Java 1.8 JDK installed and configured properly since coding for
Android is done in Java (I am told that Groovy also works, but I
haven't looked into seeing what's required to be able to code for
Android in it).
Next, you'l want to install Android
Studio. Since I was working through the Udacity Android course before starting this project, I had Android Studio installed already.
Run the Android SDK Manager (Tools
> Android > SDK Manager) and install the following
packages:
API 19
Build-tools 21.1.2
These two packages will be required by
the tools we'll install later.
Hardware:
Now that your IDE is installed, you'll
need to enable developer mode on your phone and/or tablet (assuming
that you want to test on hardware as well as in the emulator). How to
do this may vary by phone maker/model, so I'll leave that portion of
the exercise to you. It should just take a quick google search.
When you have enabled Developer Mode,
go into Settings > Developer Options and make the
following changes:
USB Debugging – ENABLED
Window Animation Scale – OFF
Transition Animation Scale – OFF
Animator Duration Scale – OFF
According to documentation, the last
three settings can be manipulated via code, but it's easier to just
turn them off in the Developer Options menu.
Open Android Studio and make a dummy
project with a blank activity. We will be using this project later,
so don't delete it after you're done with this step. When
the project is created, follow the directions outlined in Step 3
under “Setting up a Device for Development” to set up your phone to be able to receive files from Studio.
The steps required to do this vary by operating system.
When you plug your phone into your
computer, it may not immediately show the dialog asking if you want
to allow your phone to communicate via USB. If you don't see the
window, don't worry. Just lock and unlock your phone a couple of
times until it gives you the dialog.
You'll need to do this for each device
you want to connect to your computer.
I've been told by one of my co-workers
that his phone occasionally disables USB Debugging, so if you are
having problems transferring your program from Android Studio to your
phone or tablet, make sure the setting is enabled. Also, Android
Studio occasionally fails to load the program properly, so you may
just have to make sure the program is uninstalled on your phone and
re-try (this happens to me on a somewhat regular basis).
Project Setup
Robolectric and Espresso:
While you can
write unit tests for android in JUnit, the tests tend to run pretty
slowly. Robolectric is considerably faster. Additionally, you can run
UI tests using Espresso. Getting this set up manually is not a
trivial task, but thankfully there is a project on github that will
help alleviate a lot of the pain.
Download the Deckard project
and follow the instructions on the page in order to get the project
imported into Android Studio. The Deckard project comes with a
Robolectric test file and an Espresso test file, both of which should
run from the IDE as well as the command line. You may need to plug
your phone into your computer in order to get the Espresso tests to
run from the command line.
If you've ever done android development
before, you will notice that the Deckard project is missing a lot
of things. It is literally about as bare bones as a TDD android
project can be and still function. We'll be fixing that later, but
for now, what we have is fine.
Go ahead and change your package names
to something that makes sense for your project and make sure the
tests still pass. Just make sure that everything is in the same
package.
At this point, the screen you get on
your phone should look like this:
Dagger:
Dagger
is a dependency injection (DI) tool for android. According to all
accounts, it is considerably faster than Roboguice (which was being
used heavily before).
Setting
up Dagger is fairly simple. Open your build.gradle file and, make it
look like this version from my git repo.
Most
of the changes are pretty self explanatory since they're just adding
gradle entries for dagger and dagger-compiler. However, there are a
couple of changes that require a little explanation.
compile
'com.squareup:javawriter:2.5.0'
is used in order to resolve a dependency conflict between Dagger and
Espresso. Both tools use javawriter, but they call for different
versions. This, along with the exclude
calls in dagger, dagger-compiler, and espresso-core forces the
application to use a version that works with both.
At
this point, most of the main tools for doing TDD in android are
installed. We'll probably install Mockito for using mocks later, but
we don't need it right now, so we'll do that when it comes up.
Hello World
Before
we write our first code example of DI with Dagger, let's make a small
change to the app in order to convince ourselves that it's really
working (this will be the basis of the code we're injecting in a
minute, so bear with me).
Open
DeckardActivity.java and add the following lines to onCreate:
TextView
t = new TextView(this);
t
= (TextView)findViewById(R.id.text);
t.setText(“foo”);
Running
your Espresso tests should now cause them to fail (that's a good sign
in this case). Open DeckardEspressoTest.java and change the
.check(matches()) statement to look for “foo” instead of “Hello
Espresso”, run the tests to make sure they pass and then run the
application in either your emulator or phone. The resulting screen
should look like this:
Now
that we know that we can change the message on the screen
programmatically and the UI tests will still pass with the new string
on the screen, we can write our first DI module. Make a Foo interface
file as well as a FooObject class file with the contents of the files
linked to on my github account. This is what we will be injecting.
You'll
also need to make a module file in order to handle the actual
injection. In this case, the file is DummyModule. Go ahead and copy
the contents of the file from my github. Note that the injects
line states the classes that the module will be injected into.
Open
your DeckardApplication.java file and make the changes needed to make
it look like the linked file on my github. At this point, I need to
point out a couple of things that will save you future headaches.
First,
while you can build the ObjectGraph by using DummyModule as an
argument directly, I am told that you will want to build it using the
getModules method for ease of testing later.
Second,
in this example, getModules returns a List of type DummyModule. If
you are using more than one module, it will return a List of type
Object. Logically speaking, you should
be able to make the example in this file return a List of type
Object, but if you try, you get the incredibly weird error that your
program can't cast type DummyModule as type Object (which makes no
sense considering that, as a class, DummyModule inherits from
Object). I have no idea why
this is the case, but it is.
Now
for the step that brings together everything we've done so far. Alter
your DeckardActivity to look like the one linked to in my github
repo. The changes from the last time that we touched the file (to
make the screen read “foo”) are using @Inject to inject an
instance of FooObject, to set a string equal to fooObject's
provideText return value and to set the text on the screen equal to
that string.
Your
Espresso test should now be red. You'll need to change the
DeckardEspressoTest.java file to check for “injected foo” instead
of “foo” in order to make it green again.
Now,
if everything works the way it should, your tests will pass and, when
you run the program on either your phone or in the emulator, you
should see the following:
You're
now set up to do TDD in android (minus the mocking tools, which we'll
get to later). The program as it stands needs a great deal of work to
get where we're going, but we're off to a good start.
In
the next entry in this series, we'll start Sprint 1 tasks.
Current
mood: Even
Current
music: The Guess Who – No Time
Labels:
Android,
Dagger,
Espresso,
Programming,
Robolectric,
software,
TDD,
Test Driven Development
Subscribe to:
Posts (Atom)













