Category: Software Development

How to Enable Custom Debugging in Release Builds

Reliably enable debugging features for yourself. Hide them from your end users. Stop using #if DEBUG.

TLDR: Use a custom Profile that includes a certificate. Install that on devices you want to enable debugging on. By detecting if that profile has been installed on the user’s device, you can enable/disable debug features that are impossible for your users to find.


At my work we use TestFlight to distribute beta builds for internal and external testing. This a great, because once you certify a build as “ready for production”, you can just release it – it’s the exact binary that you’re going to ship.

But there’s a problem – we also want to give ourselves some extra debugging controls in our TestFlight builds for things like switching to a staging environment, toggling feature flags, and viewing extra error info.

These are features that we can’t ship to end users, but that we want available when we use the same version of the App they will get.

Person Debugging an iPhone App

I first thought of a few not-so-good ways to do this:

Bad Way #1: Use a different build configuration

Use #if DEBUG or #if TESTFLIGHT compiler directives to compile out the debug features from your release build, then only test on a version that has them included.

The upside to this approach is that your debug features are literally compiled out of your release builds so that can’t possibly be enabled by end users. On the other hand, this can also be a problem.

Its easy to enable debug features for DEBUG builds, but this doesn’t meet the original requirement of being available in TestFlight since the TestFlight builds are releasable binaries, and are compiled using the RELEASE configuration.

You could even create a custom third configuration called TESTFLIGHT which is identical to RELEASE and then enable the features in that build, but then you would need to upload two builds to TestFlight, come up with a wacky versioning scheme to identify them, and keep clear which on is which when testing and releasing. Perhaps worst of all, we wouldn’t actually be testing the binary we were releasing and sometimes there really is a difference.

Bad Way #2: Hardcoded UserId’s

Hardcode a list of our team’s userIds, and only show these features to those users.

This is bad because you need to maintain this list of privileged users, and you can’t ever add users to an old commit. What if you hire new QA and ask them to test this version? They won’t be able to with their own credentials.

It’s also just inelegant. We can do better than this.

Bad Way #3: Detect the installation source programatically

Write your own method that figures out where the App was installed from.

There is no official platform API for differentiating whether the App was installed from Xcode on your dev machine, the App Store, or TestFlight.

This is perhaps because its not a straightforward question – what if you update from an App Store build to a dev build? That probably should count as a DEBUG build, since that’s where the App binary came from, but there will still be remnants of your App Store past on the device, such as the receipt that indicates your purchase history.

But this is not to say you can’t (even if you shouldn’t) make such an API yourself!

You could implement a isRunningInTestFlightEnvironment() function, along these lines and then use that to show/hide your debugging UI.

But do not overestimate how reliable this kind of method is. It is a hack, implemented by looking for those App Store Receipts, and there’s nothing to say that Apple won’t change the name, location, or meaning of that hardcoded string in a future release, leaving your solution broken.

It is totally great for analysis and you might want to include this in logging, or event reporting, but don’t trust it blindly.

Bad Way #4: Use a secret gesture

Create an undocumented gesture recognizer, and hope its obscure enough that end users don’t stumble on it.

This is sort of like the Konami Code (⬆️⬆️⬇️⬇️◀️▶️◀️▶️🅱🅰). Who could possibly figure out something like that? … right? 😂

The problem is exactly that – someone will figure it out, and then all your debugging tools are out there with no way for you to disable them.

Bad Way #5: Use a secret URL scheme

Add a URL Type to your App target, and implement application(_ open url: options:) in your AppDelegate. Open your special URL in Safari to enable debugging.

In my opinion, this is also a pretty good option, and depending on the choice of URL is probably obscure enough that users won’t find it.

But if they do… (see above).

It’s also kind of annoying to have to exit the app and remember the special URL every time you want to change a debug setting, or enter your special menu or whatever.

This need to exit the App could also be detrimental – if you need to be able to open the debug panel without putting your App in the background, this option won’t work for you.

One advantage here is that it isn’t coupled to any single part of the App. The url opens equally well whether you’re on the login screen, or deep in the heart of your App.

The Good Way: Detect a Profile Installed on the Device

Create a special configuration profile using Keychain Access and Apple Configurator 2, and install this on your devices. Detect the presence of this profile to enable your debugging features on that device.

This solution has a number of advantages:

  • Nobody can just guess your secret gesture or cheat code, they would need to be explicitly given the debug profile.
  • You only have to install the profile once. You don’t have to do your secret enabling technique on every App run or installation.
  • You could use one profile for many apps, or multiple profiles for different levels of privilege or feature sets.
  • It applies equally well to DEBUG and RELEASE builds, and allows you to access these features even in your published App Store builds.
  • It doesn’t require any special environment, or specific users

The disadvantages I see are:

  • It requires the work of creating and storing this Debug Profile.
  • Checking the trust result of the certificate is, admittedly, a bit of a hack.
  • You need to save your Debug Profile somewhere and not lose it

So How Does This Work?

This solution is entirely based on the the SecTrustEvaluate() function.

We use SecTrustEvaluate() to determine if a user has installed and trusted a certificate (by installing our debug-enabling configuration profile), or just the plain untrusted certificate that was bundled with the Application.

So we’ll need to learn how to: create both of these certificates, create and install the configuration profile, and check the certificate for trust.

Let’s do this!

1. Create a Certificate Authority

  • Open Keychain Access.
  • Click Keychain Access > Certificate Assistant > Create a Certificate Authority.
  • Give the certificate authority a Name.
  • Recommended: extend validity.
    • Select Let me override defaults.
    • Unselect Make this CA the default.
    • Change Validity Period to up to 7300 days (20 years).
  • Choose a profile.
  • You can just leave defaults for the rest of the options. Next, Next, Next…
  • Click Show CA Certificate to verify this succeeded, then close the Certificate Assistant.

2. Create the Leaf Certificate to bundle with the App

  • Click Keychain Access > Certificate Assistant > Create Certificate.
  • Change Identity Type to Leaf.
  • Change Certificate Type to Custom, and choose ~/Library/Application Support/Certificate Authority/<your CA name>/<your CA name>.certAuthorityConfig
  • Recommended: extend validity.
    • Select Let me override defaults. Continue.
    • Change Validity Period to up to 7300 (20 years).
  • Select an Issuer: <your CA name>
  • Accept the defaults for the rest of the options. Next Next Next…
  • Destroy the private keys for both the Certificate Authority and Leaf Certificate – you don’t need them anymore.

Ok now we’ve got the raw materials needed, and we just need to convert them to the formats needed for the App.

3. Export Leaf Certificate .cer and Add to App Bundle

  • In Keychain Access, find the Leaf Certificate “certificate” 
  • Right Click > Export
  • Select File Format .cer
  • Export to a location in your project repo (you want this certificate tracked in source control)
  • In Xcode, Add Files to <project>
  • Select your App target in “Add to Targets:”

The certificate will now be included in your app bundle, but since the user hasn’t taken any action to trust that certificate, its SecTrustResultType (in the code sample below) is kSecTrustResultFatalTrustFailure.

This will be our indication that the user does not have the debugging profile installed on the device.

4. Export CA Certificate .cer and Add to Debug Profile

  • In Keychain Access, find the Certificate Authority “certificate”
  • Right Click > Export
  • Select File Format .cer
  • Export to a Location in your project repo
    • Again, you want to track this file in source control so you never lose it, but you might want to keep it in a new directory from the leaf cert to avoid confusion.
  • Do NOT add this file to your App target.

5. Install Custom Profile on Desired Phones

  • Download Apple Configurator 2
  • Click File > New Profile
  • Provide a Name and fill in any relevant General details
  • In the Certificates tab, click Configure
  • Select the <CA Certificate>.cer file you exported in Step 4
  • Save the profile, ie “MyDebugProfile”

6. Add Objective-C Class to check status of profile

Add the following function somewhere in your project. You might add it to an existing utility, or create something like a DebuggingDetector.

// Based on https://stackoverflow.com/a/31570347/782278
- (BOOL)IsMobileConfigInstalled {
    NSString* certPath = [[NSBundle mainBundle] pathForResource:@"YOUR_LEAF_CERTIFICATE_NAME_HERE" ofType:@"cer"];
    if (certPath == nil) {
    	return NO;
    }
    NSData* certData = [NSData dataWithContentsOfFile:certPath];
    if (certData == nil) {
    	return NO;
    }
    
    SecCertificateRef cert = SecCertificateCreateWithData(NULL, (__bridge CFDataRef) certData);
    SecPolicyRef policy = SecPolicyCreateBasicX509();
    SecTrustRef trust;
    
    OSStatus err = SecTrustCreateWithCertificates((__bridge CFArrayRef) [NSArray arrayWithObject:(__bridge id)cert], policy, &trust);
    print("Error Status?: \(err)")
    
    SecTrustResultType trustResult = -1;
    
    err = SecTrustEvaluate(trust, &trustResult);
    
    CFRelease(trust);
    CFRelease(policy);
    CFRelease(cert);
    
    print("Error Status?: \(err)")
    if(trustResult == kSecTrustResultUnspecified || trustResult == kSecTrustResultProceed) {
    	return YES;
    } else {
        return NO;
    }
}

7. Install the profile on the debugging devices

  • In Configurator, select the Device
  • Click Add, and choose the <MyDebugProfile>
  • The device will advise you the profile has been downloaded, but you’re not done yet.
    • At this point the trust result is still kSecTrustResultRecoverableTrustFailure
  • Open iPhone Settings then go to General > Profile
  • Choose <MyDebugProfile>
  • Tap Install, enter passcode, confirm Install, Install, Done.

The trust result will now be kSecTrustResultProceed. You have successfully enabled debugging on the device!

Even More Options…

There are surely many ways to skin a cat on this one. Here are some other ideas I’ve dreamt up but haven’t tried:

  • Leave the debugging features plainly available and protect them behind a password or PIN code.
  • Enable debugging by remotely sending messages to your App from another device such as Apple Watch or a computer.
  • Write a separate App with the debugging features and link your Apps to each other (think of how Google Drive and Google Docs are able to coordinate between Apps, for example)
  • Key debugging off of some other metadata you feel you can trust (only on your home wifi? Only on a given MAC address or SIM card? These are both probably bad ideas, but something along these lines…)
  • Write a separate App that simply enters a value into your device’s Keychain. Then check for that value in your primary App. Distribute your enabler App through TestFlight.

Do you have better ideas for how to do this? Did I miss something obvious? What else have you tried? Leave a comment below!


This article contains solutions described in this StackOverflow post and this Dev forums post.


If your job includes code review, consider also reading: How to do High-Bar Code Review Without Being a Jerk

Using a Swift PropertyWrapper to ensure a closure is only called once

Learn: how to write a property wrapper that avoids boilerplate for your asynchronous callback code.

The Solution Code

Want to skip to the answer? Variables marked with this property wrapper will be destroyed after they are read once. Use with caution!

@propertyWrapper
struct ReadableOnce<T> {
    var wrappedValue: T? {
        mutating get {
            defer { self._value = nil }
            return self._value
        }
        set {
            self._value = newValue
        }
    }
    private var _value: T? = nil
}

Now lets see how we got here.


Avoiding multiple invocations of a completion block

Let’s say we have a class that does some work, and then invokes its completion block.

class NumberChecker {
    private var completion: ((Error?) -> Void)
    
    init(completion: @escaping ((Error?) -> Void)) {
        self.completion = completion
    }
    
    public func check() {
        if someCondition {
            handleFailure(NSError(domain: "MyDomain", code: 1))
        }
        handleSuccess()
    }
    
    private func handleSuccess() {
        self.completion(nil)
    }
    private func handleFailure(_ error: Error) {
        self.completion(error)
    }
}

This code has a secret bug in its check method. Did you notice it?

    public func check() {
        if someCondition {
            handleFailure(NSError(domain: "MyDomain", code: 1))
        }
        handleSuccess()
    }

If someCondition gets evaluated as false, the completion block will be invoked twice – once by handleFailure(), and again by handleSuccess(). This is a bug – the completion block should only get called once.

Now obviously in this trivial example you could fix this by just using an else statement. But in The Real World, your code may be much more complicated, and it may be harder to make the calls of the completion block mutually exclusive.

Perhaps your code is asynchronous, and completes when the first of two operations returns a result, for example.

I’m not judging how you have gotten yourself into this problem – just providing a neat solution.

How to solve this problem

Our approach here is going to be to ensure the completion block can only be called once by destroying it once it has been used. One perfectly reasonable approach to this solution is as follows:

class NumberChecker {
    // Make the completion closure optional
    private var completion: ((Error?) -> Void)?
    
    init(completion: @escaping ((Error?) -> Void)) {
        self.completion = completion
    }
    
    public func check() {
        if someCondition {
            handleFailure(NSError(domain: "MyDomain", code: 1))
        }
        handleSuccess()
    }
    
    private func handleSuccess() {
        self.completion?(nil)
        self.completion = nil
    }
    private func handleFailure(_ error: Error) {
        self.completion?(error)
        self.completion = nil
    }
}

Here we are simply setting the completion closure to nil after we use it. This is fine, but requires us to add extra boilerplate around each usage of the callback. It is also error-prone, since we could easily forget to include the self.completion = nil and end up with a partial solution.

So instead, lets create a property wrapper that changes the behaviour of the getter for that variable.

What is a property wrapper?

Property wrappers are structs that use Generics to define extra behaviour around the usage of some arbitrary property. They are defined separately from the property they act on, and can be re-used easily for different properties, even of different types.

NSHipster has a great write-up on the background and purpose of this Swift feature if you haven’t seen it before so I won’t go into more of those details here.

The ReadableOnce property wrapper:

I included this at the top, but lets look in more detail at our property wrapper:

@propertyWrapper
struct ReadableOnce<T> {
    var wrappedValue: T? {
        mutating get {
            // This defer is the magic. It lets us 
            // erase the value AFTER it is returned.
            defer { self._value = nil }
            return self._value
        }
        set {
            self._value = newValue
        }
    }
    // Use a private backing variable that we can modify here.
    private var _value: T? = nil
}

The property wrapper definition is just a struct marked with @propertyWrapper. It has a special var called wrappedValue, which is the place we have to implement whatever our custom logic is.

In our case, we want to put our magic into the get method of the value, so that the value is destroyed after being returned the first time. The set method should behave as normal, so we just add a private backing variable, here _value.

The defer keyword

Deferred closures are executed at the end of the scope they are declared in. In our case, the defer keyword allows us to execute our cleanup code after the return value is returned.

Using @ReadableOnce in the NumberChecker

Here’s what our NumberChecker looks like if we use our new property wrapper:

class NumberChecker {
    @ReadableOnce private var completion: ((Error?) -> Void)?
    
    init(completion: @escaping ((Error?) -> Void)) {
        self.completion = completion
    }
    
    public func check() {
        if someCondition {
            handleFailure(NSError(domain: "MyDomain", code: 1))
        }
        handleSuccess()
    }
    
    private func handleSuccess() {
        self.completion?(nil)
    }
    private func handleFailure(_ error: Error) {
        self.completion?(error)
    }
}

It would be a good idea to fix that secret bug, but even with it this code will now work as expected.

Downsides of this approach

The biggest concern with this is precisely the magic that makes it useful. It is unintuitive that you can only read once, and that reading could mutate the value.

The following code demonstrates the most likely pitfall:

private func handleSuccessPoorly() {
    if self.completion != nil {
        // The completion was just read (and set to nil).
        // So the next line will silently do nothing!
        self.completion?(nil)
    }
}

Overall, I think this property wrapper is very useful, as long as it is clear to the developer that it is being used.

So basically:

With great power comes great responsibility.

Uncle ben. yes. from spiderman.

Possible Improvements

There are a number of things you might continue to experiment with to improve this solution:

  • Use a different property wrapper to accomplish the same task
  • Implement something like a callThenDestroy() method on the type you want to store
  • Try @dynamicCallable (see this Hacking With Swift post for info)
  • Thread safety has not been considered in this implementation. It would be possible for two threads to both get access to the variable before either of them set it to nil.

This article is based on a code sample shared with me by Josh Caswell (https://github.com/woolsweater)


Have you tried any of the above? Do you have a better solution? Please leave a comment below!


If you liked this post you might also like my guide on how to do high-bar code review without being a jerk.

When is it *good* for your App to crash?

Most of the time, a crashing app is the very last thing you want. Obviously.

But sometimes you really do want your app to crash to enforce correct usage of your APIs.

What’s worse than a crash?

Crashes are obviously bad – it’s disruptive to your user, they might lose data or work in progress, it makes them feel frustrated and less likely to open your app again, and it just smells like bad quality.

But there’s something worse than a crash: undefined behaviour.

Our programs are carefully thought through, and crafted so that the right input data is manipulated, used for calculations, stored, outputted, and otherwise trusted. But this also makes them brittle. Whether you realize it or not, all of these complex behaviours only work when you constrain your code to be used with some predefined, known range of inputs.

When your code is given unexpected types of data, you can’t control what will happen – it’s outside of what you designed and tested for. You could end up accidentally corrupting user data, opening security holes, or (the most likely) providing inaccurate and confusing information to the user.

In some cases, these things are worse than crashing.

  • What if your banking app tells the user they have more money than they really have and then they suddenly can’t pay rent!
  • What if your app ends up in a state where none of the buttons do anything and you can’t navigate out of that screen?

There are of course others, but these user experiences might actually be *worse* than crashing. So crash! The user knows something went wrong, and needs to restart the app to fix it.

Crashing as a Development Tool

Crashing is also extremely useful in teaching developers how to use your APIs.

A (contrived) example

Lets say you’re writing the official calculator app for a university. It’s very important that it do math correctly, because all of this university’s students will be forced to use this app for all of their exams.

Ok so your inputs are numbers and operators.

Let’s imagine you had some calculator UI, with logic that looks something like this:

We could write a custom UITextField subclass that only supported numbers that could be converted into a float.

class CalculatorUI {
    @IBOutlet var input1: NumericTextField!
    @IBOutlet var input2: NumericTextField!
    @IBOutlet var result: NumericTextField!
    
    /// Takes the inputs, adds them, and updates the result TextField.
    @IBAction func didPressAddButton() {
        result.floatValue = input1.floatValue + input2.floatValue
    }
}

This super clean, simple class relies on pushing some of the complication into the NumericTextField:

// Basic Implementation
class NumericTextField: UITextField {

    var floatValue: Float {
        get {
            if let acceptableValue = self.text as? Float {
                return acceptableValue
            } else {
                // What do we return here?
                return 0
            }
        }
        set { self.text = "\(newValue)" }
    }
}

There are a couple problems with our basic implementation:

  1. We always return 0 if the text can’t be converted into a float. If a student somehow types non-float values like "two" and "two" as their inputs, they might not realize this isn’t supported, and actually believe that their answer is 0! This answer is not correct! AHH
  2. The NumericTextField class design encourages misuse! There is nothing preventing us from trying to access the .text property directly, but the whole point of this custom text field is to restrict the types to be floats.

Clearly, the real solution here is to show some error message that tells the user to only type numbers, or better yet to use a numeric keyboard style so enforce that they can’t enter alphabetical characters.

But we still want to improve this text field’s implementation.

Crashing is a hint to the developer!

The key thing here is that both of the problems described above are developer errors. They are misusing the API. We want to catch these mistakes as early into the development process as possible. And the best way to call attention to them is to crash.

Yep. We’re going to crash. On purpose.

Solution #1: Catch the invalid text field values

If the text field’s getter method is unable to convert the text to a Float, that is because the developer has allowed the user to input letters.

class NumericTextField: UITextField {

    var floatValue: Float {
        get {
            guard let acceptableValue = self.text as? Float else {
                fatalError("The NumericTextField assumes that is always has a text value that can be converted to a Float.")
            }
                
            return acceptableValue
        }
        set { self.text = "\(newValue)" }
    }
}

Using Swift’s fatalError lets us supply a crash message, which is much clearer than enforcing this behaviour via a force-unwrap:

class NumericTextField: UITextField {

    var floatValue: Float {
        get {
            // BAD!!  Don't use ! (force-unwrap)
            return self.text as! Float
        }
        set { self.text = "\(newValue)" }
    }
}

Solution #2: Prevent usage of the superclass’s API

The second thing we’ll prevent is usage of the UITextField.text property.

We can do this by overriding it and crashing.

class NumericTextField: UITextField {
    
    override var text: String? {
        set { fatalError("Use `floatValue` instead.") }
        get { fatalError("Use `floatValue` instead.") }
    }

    var floatValue: Float {
        get {
            guard let acceptableValue = super.text as? Float else {
                fatalError("Our error message...")
            }
                
            return acceptableValue
        }
        set { super.text = "\(newValue)" }
    }
}

Notice that we’ve also changed the floatValue getter and setter to use the .text property from the super class rather than self, since calling self.text will now crash.

We’ve now made it clear to the developer (or our future self) what the allowable usages of our class are.

Crashing in development is better than undefined behaviour in production.

With these two changes, we’ve made is easier for developers to use our API correctly.

This isn’t something you want to do on all of your APIs. You don’t want to allow anything external (such as user input or server responses) to be able to crash your app.

But the case above shows how you can use crashing as a tool to make sure that your code is using your other code correctly.

11 Habits of an Effective Developer

Being a developer is hard – and being an effective developer demands that you juggle many responsibilities at the same time – from writing and reviewing code to mentoring others and communicating with stakeholders.

How are you supposed to be good at all of these things? Each one is hard even just on its own!

The answer – build good habits.

Then you don’t have to actually remember to do the things, you just stick with your habits.

So here are some habits that I believe can make you a more effective developer. Practice these, and you will be the most valuable member of your team.

1. Read

Reading makes you smarter. There’s just something about it. Obviously you learn information from the content you are reading, but its also exercise for your brain.

I’ve been back in the habit of reading for the past year or so, and I feel it making my mind a little fresher – a little sharper.

Set yourself a reading goal to hold yourself accountable – this year my reading goal is to read 15 books – 7 books for fun, 5 books for personal development, and 3 books for work (books about software).

2. Log Your Daily Standup

You’re probably already doing regular standup meetings at work. These are ubiquitous in the software industry now.

But I also find it beneficial to write down my plan for the day every day. This has at least three benefits.

First, I can keep that in front of me all day to help stay focused and on-task.

Second, I can refer to it the next morning while I struggle to remember what I did the day before 😂.

Third, I can refer to it at the end of the year when I’m asked to reflect on my work for an annual review (or to help brainstorm for the day you eventually need to update your resumé.

3. Keep the main thing the main thing

This is a great quote:

To succeed, always remember that the main thing is to keep the main thing the main thing.

Brendon Burchard, High performance Habits

And its true.

It is too easy to get lost in the weeds as a developer – you can spend a huge amount of time on something that is in some ways productive and helpful, but might not be actually delivering value to your employer, or related to the high priority task you need to get done before some deadline.

Writing unit tests is a great goal; but writing unit tests for a component that’s not used probably isn’t the main thing you’re supposed to be working on. You just found yourself in that code and noticed it needed cleaning up.

Keep the main thing the main thing. Stay focused.

4. Use your daily commute time for something productive

This is as important as your commute is long. In my case my commute to work is about 2 hours.

Yep, 2 hours. Each way.

Luckily I’m not driving, but it is all too easy to spend 4 hours a day playing Angry Birds, or scrolling through Instagram. And that would be a colossal waste of time.

I spend one of those trips doing something for myself, like reading (see #1), or writing (see #11). And I spend the other trip doing work for my employer, who allows me to end my work day on the commute to avoid getting home suuuuper late.

But it’s a game changer.

Suddenly you have time – already carved into your schedule – to tackle that thing you’ve been meaning to do. Do it on your commute!

5. Attend tech talks or meetups regularly

If you’re not constantly learning in this industry you’re falling behind. It is one of the greatest things about working as a developer that you get to keep learning throughout your career, but it is hard to know where you should be focusing your learning.

Public tech talks and meetups expose you to industry trends and technologies you haven’t seen before. They also are great opportunities to slowly and casually build a network of likeminded (and potentially useful) connections with others in your industry.

You can also accomplish some of this by joining public Slack groups – but there’s still no substitute for chatting face-to-face – IRL.

6. Document your accomplishments, and make quantitative measurements

I learned this from the Manager Tools/Career Tools podcasts. In an episode where they are discussing building a resumé, they recommend keeping a career file – a document where you keep extensive detailed notes on accomplishments you’ve had at your job.

The career file can include a wide variety of things, but they should be quantitative, and should demonstrate the skills you have. Imagine if you could show your usage of these skills, instead of just listing them on your resumé – it’s much more compelling.

Did you increase the performance of a critical component of code? By what percentage? And in how many days/weeks? Was it ahead of schedule? Did this cut costs for the business? Did customer complaints reduce as a result of your work? Did it enable sales to land a big deal?

These are all quantifiable evidence of how great you are for the company you work for – and an effective developer knows why they are valuable.

7. Keep your computer organized

This is as simple as it sounds.

Your job is complex enough – don’t add another layer of complexity by making your computer hard to use. Know where things are – have a system for naming files or organizing directories.

8. Review your own pull requests

Whenever you submit a pull request and ask your peers for review, you are asking for a bit of their time to double-check your work. You should be confident that it is as good as you could make it.

Don’t be that developer that submits a half-done pull request (unless you’re just asking for early feedback and in that case state that intent explicitly).

I like the interface of a pull request to look over my work, so sometimes I’ll create the pull request but not add any reviewers until I’ve reviewed it myself. I can even make comments to myself there, or go through file by file to make sure I’m not leaving little debug messages for myself in my final commits.

9. Commit early and often

Git will save your life some day. Well ok, maybe not literally. But it will be your safety net and allow you to quickly revert back to a working version, or refer to that intermediate step you made while working through a problem.

But only if you committed your work while you went.

There is no reason not to commit often – even if you want to avoid cluttering up git history you can squash commits together at the end.

At a minimum you need to commit your work at the end of every day before you go home, and push to the remote. If your computer implodes, or gets eaten by a lion, your work will be safe, and you won’t have to scramble to remember exactly how you solved that difficult problem so elegantly last week.

10. Schedule exercise into your daily routine

Our job is very sedentary. This is terrible for your health over the long term, and makes you feel sluggish and depressed in the short term.

This is a simple life hack. When you get exercise you have more energy. Fact.

So ensure you have exercise in your routine. At least half an hour. Every. Single. Day.

Personally, I choose to use a rental bicycle for the last leg of my commute instead of taking the bus. It is often actually faster than transit, plus I feel awake and fresh when I arrive at the office.

You can also find yoga videos on YouTube in the morning/evening, or even just go for a walk at lunch every day.

There’s no excuse, and you’ll feel SO much more powered up to accomplish all the things in your busy life.

11. Write when you learn a new thing

Remember how when you were in school and your teacher taught a lesson, you had to write down what you learned and submit assignments demonstrating your new knowledge?

That’s not just so the teacher can mark you.

More importantly, it helps you internalize that learning, and commit it to long term memory.

Maybe you can start a blog, or maybe you just keep a journal or some notes. But writing (either by hand, or on the computer) can help take your learning to the next level.

So Many Habits!

You can’t take all of these on at once – overreaching on too many things at once is a recipe for failure. But start with one of these behaviours, and do it every day. Then after a month, once that behaviour has ingrained itself into you as a habit, start the next one.

After a year, you’ll have all of these working for you, powering you to be a more effective developer version of yourself!

Go get it!

Why Agile-at-Scale is So Painful for Developers

Does you use the Agile methodology at work? What do you think of it? Do you think it helps you be more productive?

I suspect your answer to that question depends on a number of factors, but the key determiners are likely the role assigned to you by the Agile framework, and the details of how Agile has been adopted by your company.

Why Agile is Supposedly Better Than Waterfall

Waterfall has a fatal flaw – although thorough planning and a long-term roadmap are useful in organizing a coherent project plan and achieving a big goal, you’re stuck with this plan once you’ve started. Projects planned with waterfall will often do huge portions of their work before they are able to grab the separate pieces of a system and try them out together.

This means that if something is wrong, you don’t find out for a long time – possibly until its too late.

And if the market changes while you’re working on this project, well that’s just too bad. You’ve already committed to making the thing you planned from the beginning. At this point you have no choice but to see it through and release your too-late product to the already-moved-on market.

Agile is primarily intended to solve one key problem in Waterfall; just as the name describes, it gives businesses the ability to change their mind quickly, and pivot to a new plan.

It accomplishes this by prescribing that work be chunked into 2-week commitments (this can vary from 1 to 4 weeks). At the end of that 2 weeks, the entire software system should be in a working (shippable) state, and everyone should be able to choose the next most important thing they can get done in the next two weeks.

This is an elegant idea. And it works for some teams, but it requires a lot of discipline, and it doesn’t scale well.

Agile for really large teams – sucks.

What Is Hard About Scaling Agile

There is a whole industry that has arisen around trying to apply the Agile methodology to large corporations. And this is a noble pursuit. It is easy to see why corporations would want to adopt these paradigms – all the little guys do it with great success, why shouldn’t they!

But agile is inherently challenging for large teams. Imagine a team of 1000 – this breaks down to at least 100 scrum teams if you keep a maximum of 10 people per team.

The projects endeavoured by these types of teams are enormous, and require detailed planning and coordination on multiple levels.

How do you simultaneously get a team of 1000 working together in the same direction on these projects, but then also be able to change the direction of the company at a week’s notice?

How do you simultaneously empower 1000 employees to feel ownership for their work while retaining the ability to change what they work on at a week’s notice?

The answer is, of course, you can’t. You need to compromise somewhere, and so these frameworks for applying Agile principles at a large scale do compromise.

Instead of being able to change the plan every couple weeks, you group sprints into sets, and re-evaluate at the end of that couple-of-months period instead. You play mini-Waterfall.

And to make sure everyone at the business level stays up-to-date on how things are progressing, you have hoards of personnel dedicated to communicating status between teams.

And this is where scaling Agile breaks down. When you introduce this much planning and coordination into agile, you lose the agility.

It’s like Heisenberg’s uncertainty principle – trying to measure a thing causes a change in that thing, and you’ve kind of ruined the accuracy of the measurement.

How your role in large-scale agile defines whether you think it’s working:

This is where we can see that your feelings on working within an Agile system will depend on your position with that system.

If you are an individual contributor, you will now spend a large portion of your time allowing others to measure you. It will go far beyond your daily standup. Every project or initiative in the company will have its own meetings to both plan and assess progress, and you will be expected to attend lots of these.

If you are a communicator in this system, you are incentivized to ask lots of questions, and have a clear picture of everything that’s going on at all times. But this incentive works against the pure productivity of the team. Every status update you give is an interruption from whatever you were working on. And each interruption takes a long time to recover from. Focus is not just lost for 15 seconds while you have a “quick check-in” – it is lost for half an hour while the you struggle to re-enter “the zone” and re-establish the deep concentration they were in.

If you are a leader or executive in this system, you are too far away from these interruptions to see their devastation first-hand. Without real proximity to the scrum teams or experience working as a contributor, you may not realize that the your effort to stay abreast of status in an attempt to make informed decisions has introduced a counter-productive vector in the system.

The need to keep executives appraised is important and fair – the business does absolutely need to understand what’s going on in order to make the best decisions about how to allocate money and resources.

But it’s also what sucks about working as an individual contributor at giant companies.

So What?

It helps to understand these things to make them less painful. Agile doesn’t have to be bad.

It is up to the implementers to avoid imposing onerous bean-counting requirements, and to protect the developers from as much of the structure of the scaling up as possible. Sprint planning is still useful! But implementing a company-wide restriction on how to type out your task descriptions is not.

If you are a communicator, know that you are the key to pulling off agile without being a pain in the butt. Communicate effectively, but try to do as much observation from a distance as possible.

If you are an individual contributor, advocate for what you need to make yourself more productive. Remember – you are being paid (probably a lot) to use a specialized skill. Try to preserve most of your time for that job.

How to do High-Bar Code Review Without Being a Jerk

How to do code review that enforces high standards while avoiding common problems on your team.

Code review is the hardest part of being a developer.

Seriously.

Whereas most of our work is between you and a computer – a machine you just have to operate correctly and indicate clear intentions to – code review is a thing between humans. And like it or not, those humans are squishy, opinionated, emotional creatures. And those traits make code review a challenge.

In principle your goal in a code review is simple: examine another person’s code and make sure their pull request (“PR”) is “good enough” to merge.

But in reality there are a gazillion other things swirling around in your brain.

  • Urgency – is this a hot fix for a major production outage?
  • Seniority – am I allowed to decline this senior developer’s PR?
  • Reaction – will someone yell at me if I decline this PR?
  • Grudges – will you decline my next PR just because I declined this one?

None of these are related to the code quality, but they are legitimate concerns – you might really rub people the wrong way if you review code unkindly.

So how do you avoid these problems? And how do you set up a team for successful code reviews?

Set the Team Up for Success

Before anyone even writes any code, set clear expectations. Anticipate the obvious problems and just avoid them.

  1. Use a code linter – many arguments in PRs are over style. And thats just not the place for it. You can easily prevent most of these disagreements from making it as far as the pull request by agreeing on a set of linter rules.
  2. Have a code style document – some things aren’t reasonably enforceable by a linter, but are still just conventions. Write these down in a central place and have everyone on the team agree to them. Getting buy-in from your team gives everyone accountability. If the team really did all agree on these rules its much easier to enforce them. All you need to do is link to that page and remind the developer that this was a team decision.
  3. Do design meetings – when starting a big task, consider having a design meeting. This might feel like a waste of time but it’s not. Having the team contribute to the design before the developer starts work will avoid large-scale issues from cropping up when it comes time to review. You don’t want to start a task over because it has a fatal design flaw. And you really don’t want to be forced into accepting a fatally flawed design because you’ve run out of time to go back and start over.
  4. Establish relationships and respect – people are more empathetic and kind when they know each other, and preferably even like each other. Invest in relationships on the team and you’ll avoid some conflicts caused by people just not taking the time to care about each other’s feelings.
  5. Write a Definition of Done – this is a formal, generic list of requirements that apply to all/most tasks. For example maybe “all code must have 70% unit test coverage, relevant documentation must be updated, the CI build must pass, and you must demo your work for your manager.” If any of these aren’t true, the PR should indicate that it is a work in progress, and it shouldn’t be allowed to be merged yet. This makes it clear the submitter is looking for early, broad feedback.

Submitting a Pull Request

Recognize that reviewing code is hard, and make it as easy as possible for your reviewers to give you useful feedback. Remember – you want them to find the problems with your PR. It saves your future self a bunch of headaches.

  1. Write a PR summary – include a brief explanation of the purpose of this PR. Link to the ticket this PR is addressing. If there’s anything odd in your solution, explain it. If there’s a reason you truly couldn’t write unit tests, give it.
  2. Add the right reviewers – don’t add your whole company. Maybe not even your whole team. Recognize that people will take time from their day to help you improve your code when you ask for their review. Just add the people who will give you the most useful feedback – perhaps the subject matter expert of the file you’re working on. Or the person who filed the bug you’re fixing. You will also likely need to add a senior developer who has the authority to accept your work.
  3. Accept the feedback – if someone provides feedback and you can’t articulate a good reason not to, just accept their suggestion. Make their requested change. This may require you to set aside a bit of the pride you have in the great work you’re submitting (and that pride is good!), but it’s not going to hurt anything to accept their suggestion, and you’ll avoid lots of unneeded conflict.

Reviewing a Pull Request

Keep these things in mind when you go to review a PR.

  1. This PR doesn’t need to be perfect. It needs to make the code better. Follow-up PRs are totally ok in many situations, as long as the current PR works correctly, and doesn’t impose an unreasonable maintenance or refactoring cost on others working in the same codebase.
  2. Start with the big picture – pull up the ticket for PR and confirm that this actually does what it’s supposed to. Does it actually work? Does it meet the Definition of Done? Is it clearly missing anything like unit tests? Provide this feedback early so that the submitted has time to rejig the whole PR if needed. There’s no sense in starting with identifying 20 typos (which the submitter may immediately start fixing) only to then suggest they take a completely different approach that will require them to throw all those fixes away anyways.
  3. Next look at the design – how have the classes been broken down? Are the components too big or have too many responsibilities? Do they define clear, testable boundaries? Provide this feedback using the verbiage of established engineering principles. “This design isn’t testable” is much less useful than “Using dependency injection here would allow you to mock out component X in your unit test”.
  4. Next review the “central class(es)” – identify the class that is the guts of the change, and start here. If there are 10 changed files in a PR, most likely only 2-3 of these contain the key logic, and the rest are helper classes or usages of the renamed API, etc. Focus your best effort on the most important classes while you still have the most energy and are providing the best feedback. Then circle back around to the other classes afterwards.
  5. Set a comment limit for yourself – It is difficult for the submitter to address 50 comments on a PR unless they are all trivial nitpicks and spelling errors. It’s also really discouraging to see that huge number of comments – it gives the impression of poor work and may really slow down the developer in future tasks.
  6. Consider in-person reviews for “bad” PRs – if there really are 50 significant flaws in the PR, take the review offline. As someone who can see these flaws, it is your responsibility to help the submitter learn. So sit down and go through the PR together, IRL. Be patient and compassionate, and don’t rush through it or make the submitter feel like they’re taking up your valuable time (even if they are). You are investing in your teammate – it will pay dividends.
  7. Consider in-person reviews for big PRs – sometimes a PR “blows up” and becomes a huge number of changes. In these cases it will likely be impossible (or unreasonably time-consuming) for a reviewer to figure out where to start. So do a group review, and have the submitter briefly present their work. The team can ask a few questions and get their bearings, and either do the full review together, or go back to separate desks and review online now that you have a better sense of what’s going on.
  8. Identify critical vs nitpicks – Be clear about which changes you think are unacceptable, and which are minor suggestions or opinions. These minor items can start with “Nit” as in “Nit: This might be clearer if you renamed this local variable to account instead of a. Also be clear if a comment is just a question, or if you’re unsure of whether your feedback is valid or not.

If They Always Make the Same “Mistakes”

Over time, you may want to give a teammate the same feedback on many PRs. This is where it becomes easy to be a jerk. Resist. Nobody wants to work with a jerk, and they certainly won’t be receptive to feedback if you treat them poorly.

  1. Remind yourself they are a professional – Nobody writes bad code on purpose. Nobody. Even that lazy sarcastic developer you don’t like for whatever reason doesn’t. So start by trying to help, and take the mindset that you are helping – not criticizing or “fixing” them.
  2. Ask yourself “is this my opinion?” – Just because you disagree with it doesn’t mean it was a mistake. Perhaps they have a valid reason for continuing with the pattern you don’t like, and perhaps that’s ok. You need to let other people win sometimes and recognize what is an opinion. There are many ways to solve all software problems, and we are constantly judging where to draw compromises. You may simply fall on opposite sides of the compromise here – if their code is functional and meets the agreements your team has set out then maybe you should just let it be and tolerate their alternative solution.
  3. Just talk about it – sometimes having a regular old human conversation about the problem will help them understand better why it even is a problem. Have the conversation at a time when they seem fresh and open to feedback, and do it in a place where they won’t feel embarrassed – don’t call them out in front of the whole team, or make them look or feel bad in front of their manager. Choose your first words carefully. “Hey I noticed something I think I can help with – do you have a second?” will be better received than “So you keep making this mistake – we need to talk about it”.
  4. Lead by example – people learn by copying each other. If you demonstrate the kind of thing you wish they were doing, they might just pick it up without you even having to confront them about it. This is also especially important if you do chat about it – make sure that you are on your extra best behaviour and demonstrate your expectations. They’ll be looking for a good example to help solidify the “right way”.

You can do it!

Reviewing code is a super important skill. It helps make your team’s code better, and makes your life better. You need to work and live with this code, so you’re making things easier for yourself by keeping a high bar in your code review. But you also need to be kind to your friends so they stay friends.

Hopefully these tips will help you be a better code reviewer! Good luck!

What is Git? Taking Control of Source Control

Let’s say you’re working on a big project – your final paper for a class at school, or a report for your boss. The project is long and complicated, you chip away at it over a period of months, and go through many many revisions of editing. 

Then you save your last copy, Big Project Final Version and hand it to your trusted friend to look over. They find 100 more little typos, so you fix those, then Save As: Big Project Final Version 2

Just to be safe you read over it one more time, and decide you actually want to go back to what you had before in a few places. Hmmm… how exactly was that worded again? 

Save As: Big Project Final Version Final. 

Save As: Big Project For Real Last One. 

Save As: Big Project FINAL. 

Then your trusted friends trips over your dog and spills coffee on your computer and you lose everything.

Does any part of this sound familiar?

What is Source Control?

Source control is a solution to this problem. Source control is basically a “saving” system. It is a program you can use to organize your saved copies, easily look back at past versions, keep multiple versions in parallel, and back the whole project up on multiple computers.

Some consumer software also includes systems that do some parts of this job – Microsoft Office and Google Docs have the ability to look at your Version History, for example.

In the programming world, the most commonly used source control system is Git. Other major ones include Subversion and Mercurial. I’ll focus my description on Git, since this is the only system I’ve ever been exposed to.

Git Commits

When you want to save a project in Git, you create a git repository (“repo”). A repo is just a folder with a couple of hidden files in it to help keep track of things.

You can add anything you want to this folder – code, images, copies of other entire programs – whatever! And git can keep track of it.

All you have to do is periodically “commit” your work. This puts a “bookmark” or a “save point” into the history, and allows you to come back to this version later if you want to.

Every time you get your work to a point you want a snapshot of you “make a commit”. This is probably a few times a day, depending on what you’re working on.

Git Branches

The most basic use of Git is as described above. You just keep a linear history of your work, and you can go backwards and forwards in time through these revisions. This is a Git history with only one branch. Customarily, this branch is given the name master, though that’s just a convention – there’s nothing special about that name.

But many projects are more complicated than this, and require keeping multiple, slightly different, versions at the same time. Some of these versions may even be mutually exclusive from each other. Take a plumber’s resumé, for example – they might keep two copies up-to-date – one for getting a job actually doing plumbing, and another version for getting a job teaching plumbing.

These projects have multiple branches. Perhaps one named plumbing, and another branch named teaching.

In Git, we choose a point in the history we want to start from (which is often just the most recent commit), and branch off from there.

Another common use of branches is to use them to work on some new feature or aspect of your project while leaving the master branch unchanged. This keeps master “clean”, and also allows you to have multiple tasks in-progress, without having to finish one before moving on to the next.

Maybe you’re storing the code for a note-taking app in a Git repo. Your next task is to add a “Share” button in your app. So you create a branch called addShareButton. Now when you make a commit, the master version doesn’t change. Instead, only this addShareButton branch has these new commits.

Once you’ve finished working on your new task in this task branch, you can merge it into the master branch, and then your master version has these changes too! You don’t need that task branch anymore, and you can delete it. Again you now only have one master version to keep track of.

Local vs Remote Repos

So far we’ve basically assumed that you’re working alone on this project, but in a work environment that’s rarely the case. You’ll likely have colleagues that work in the same codebase, and want to make changes to the same files as you – often at the same time as you. So how do you do this without tripping all over each other?

Well first lets back up a second to talk about how git is a distributed version control system. This means that everyone can have a copy of the full project history. If you and one colleague are working on a project together, you can both have a copy of the Git repo, and Git will allow you to keep those copies in sync with each other. But neither of those copies is technically “the primary copy” – they are equal. The project has just been distributed between multiple machines.

Now with that said, it is still useful to pretend that one copy of the repo is the main copy – especially when working with teams – and most teams do this.

In addition to everyone having a copy of the repo on their development computers, there is also a copy on a server that is accessible to everyone. This centralizes and organizes your work – everyone can agree to treat that as the “master copy”. When an individual developer finishes a task, they commit their work, and push their branch it to this remote server (usually just called “the remote” or “origin”. Then this branch can be merged into the master branch.

There are a couple key benefits of having your work on the shared remote, and having everyone push to the same centralized repo:

  • It is easy for everyone to keep up-to-date with everyone else’s work by simply pulling the latest changes from that central repo into their copy on their machine. Without this you would have to separately push your work to each of their machines individually!
  • Work is less likely to be lost if your machine dies unexpectedly
  • It is clear when work is done – it is done when it has been merged into the master branch on the remote.

There is also one other key benefit, which is immensely powerful and makes life much easier:

  • The centralized repo can be managed through GitHub or Bitbucket or other similar software.

Pull requests, code review, feedback sessions

Github has many capabilities. It provides a nice interface for browsing all of the commits and branches in the repo, lets you look at a history of how those changes relate to each other, and is also a directory of millions of projects.

But perhaps the most useful feature it provides is the ability to create Pull Requests.

A pull request is a way of requesting that your branch get merged into the master branch (or any branch). It shows a summary of the changes (which you call the difference (or “diff”) between the branches, and allows your peers to review your work and leave feedback on it before it gets merged.

A reviewer can leave comments/questions, mark their approval, decline your pull request (which temporarily rejects it while you address their feedback), or merge your pull request.

This tool will dramatically improve the average code quality in a repository – partly because people’s feedback will yield useful suggestions, and partly because you try harder when you know someone will be looking over your code with a fine-toothed comb. If you cut corners or do things you know are the lazy way or just plain wrong, someone will probably find those mistakes.

So it holds you accountable for your work, but also is a safeguard around you. Your work is no longer your sole responsibility – you can rely on your team to help spot the mistakes you missed.

Of course this also means that there is more work for you to do – you need to review other people’s code too. And let me tell you – this is often harder than writing the code yourself. You need to try to understand what that code is doing from what little context you can see in the pull request. This is often impossible, or just a waste of time, and a better approach may be to have a real-life conversation with the code’s author, or sit down together to go over the changes.

This can also help ease tensions if you think that the code in the pull request has some fundamental flaw, or you’ve spotted so many errors that writing them all down will overwhelm, discourage, or publicly embarrass the author. These are all bad things to do to a person – and you need to remember to be kind when reviewing someone’s work. They wouldn’t have submitted it for review if they thought it was awful.

Let’s Git This

Git is a powerful tool for managing work shared across a team. It lets you look back in time, see what each other are doing, and make a clear path for how to call something done.

Add Git to your repertoire – it will make you an attractive job candidate, and a better developer. With no need for saving your file versions in separate folders, it will make you more organized. 

And some day it might just save you from a world of hurt when you spill coffee into your computer.

A Day in the Life of an iOS Developer

An iOS developer probably has a similar day to most other kinds of developers – it includes reading and writing code, discussions with teammates, and maybe some meetings. But what exactly does that look like?

Well here’s a rundown of a typical day for me (if I’m not working from home):

7:00 – Alarm goes off. Get up, eat breakfast and make coffee.

7:30 – Leave the house. Head to the train station and start the commute. I live pretty far from work, but taking the train allows me to make use of the travel time and avoid sitting in traffic. I also bicycle from the train station to my office, so I get some exercise in as well. While I’m on the train I read or write, or get an early start on my work for the day.

The Art of Cautious Self Improvement

Recently I’ve been spending a lot of time thinking about self improvement. Some of this has been about creating better habits and making better or healthier decisions in my personal life, and most of this has been concentrating on work – how do I become a better developer, a better leader, a better professional.

But as I’ve been going through this, I’ve found that on the basis of aiming for self improvement, you first accept that there are things to be improved. And when you make this acknowledgement, not all the feelings that come out are positive, growth-y, or productive.

Starting Somewhere

My first instinct on self improvement is very literal – get better at something by educating myself and practicing that new skill.

So over the past year I spent time reading Robert Martin’s famous programming books: Clean Code, Clean Architecture, and The Clean Coder. These make up a portion of what Prentice Hall publishing calls “The Robert Martin Series” (clever naming pattern, eh).

And honestly, these books are fantastic resources, as advertised. Like, truly will make you better at your job, guaranteed.

The more I read these, the more I feel they are articulating a summary of critical feedback I’ve received over years of code review sessions, but packaged in an accessible and useful way!

The Dark Side

But there’s a downside to having these deep mysteries of the software universe articulated in such a way that they feel obvious.

Now that I’ve been exposed to the light and “know better” than to make the mistakes I made last year, I feel like following these rules should be as simple as reading them – and its not.

The Clean Coder is a book about being a “software professional” – not a technical programming book. So the arguments Martin makes are about how to differentiate between being a “good” employee/teammate/professional and being a “bad” employee/teammate/professional. How to make the optimal decision in a range of situations.

He makes it seem that there is a right and a wrong solution to every problem – like math, or software. A true professional finds that optimal solution, and executes it – and if you behave in any other way, you are being bad (at your job, or to other people).

I’ve found these lessons in The Clean Coder really hard to convert to real life in a constructive way.

And I think there’s a major piece missing in the book – the piece where you take the time to actually make these self-improvements.

Making Commitments – a study in failure

Martin devotes an entire chapter to the differentiation of estimates from commitments, for example.

He cautions that you should make sure that when asked for an estimate you make it clear that its an estimate, and then provide that estimate in the form of a probability distribution (it is an “estimate” after all). But “commitments” are different. It’s a powerful word, that should only be used sparingly and carefully.

“Professionals don’t make commitments unless they know they can achieve them

The cost of missing those commitments, to [colleagues and the business], and to your reputation, is enormous. Missing a commitment is an act of dishonestly only slightly less onerous than an over lie.”

Robert Martin, The Clean Coder

How are you supposed to read this and not feel like a dishonest professional, and a liar? I’m sure everyone can name countless times where they made “commitments” they’ve failed to follow through on, and I am no exception here.

Even after reading this chapter, and after making a big deal in a meeting about the difference between an estimate and a commitment, I still don’t have the hang of this – I failed to meet my very next commitment because an external force came up. But that was supposed to be the point of the “commitment” – you only commit when you’ve accounted for all those things”.

So what does this mean?

It’s ok

Well, honestly, I don’t think it means I’m unprofessional, or dishonest, or a liar (even though those feelings are super palpable and vivid). I think it means I’m recognizing this as a problem now, and opening the window to make the change in that behaviour. I’ve spotted it, in the wild, in its natural habitat (the infamous sprint planning meeting).

I think this recognition phase is where we need a little sprinkle of grace, and to allow oneself the opportunity to fail – a few times if necessary. Probably many times.

Applying the lessons from The Clean Coder requires us in some cases to break deep-seated habits, or fight our instinctive and emotional reactions. That’s super freaking hard. Especially when we’re under pressure, annoyed about something else, or over-tired.

So I’m not saying you shouldn’t read The Clean Coder – you absolutely should. It will make you better at your job, and a more professional software craftsperson.

But you shouldn’t expect to get to the end, mind-meld the information into your life and immediately be better.

Instead, take the lessons from this book, and give them prime real-estate in your brain. Then let them guide you in these situations, and point you in a direction that is better than you would’ve gone yesterday. Then do the same thing again the next day.

Over weeks and months, you will become a better employee/teammate/professional – just as long as you give yourself the chance to fail along the way.

Clean Architecture – SOLID Design Principles Summary

Clean Architecture is a book by “Uncle Bob” Robert Martin as a followup to his popular Clean Code. It includes a brief section about the SOLID principles, which are the touchstone of his programming philosophies, and have been described in his other books.

This summary is a cheatsheet to help you remember the SOLID principles in your day-to-day work.

 

The SOLID Principles

  • Single Responsibility – each module should only have one reason to change
  • Open-Closed – Design software so its behaviour can change by adding code – not changing the existing code.
  • Liskov – Use interfaces/protocols to separate interchangeable parts
  • Interface Segregation – Dont depend on things you don’t use
  • Dependency Inversion – High-level code shouldn’t depend on low-level implementation

Powered by WordPress & Theme by Anders Norén