Slide 1

Slide 1 text

Planned Obsolescence Built to Last, or Build One to Throw Away? OSCON 2013 Baq Haidri • @baqhaidri Ian Dees • @undees

Slide 2

Slide 2 text

Baq making things work at scale

Slide 3

Slide 3 text

discoverable REST APIs http://rest.li/

Slide 4

Slide 4 text

Ian tinkering with hardware and software

Slide 5

Slide 5 text

http://pragprog.com/titles/dhwcr discount code: CucumberIanDees

Slide 6

Slide 6 text

So, planned obsolescence

Slide 7

Slide 7 text

OSCON 2012

Slide 8

Slide 8 text

— Should software makers apply the planned- obsolescence strategy? — Like, a self-destructing Github repo? — More like designing things for the correct lifespan.

Slide 9

Slide 9 text

Computer programs are... machines with far more moving parts than any engine: the parts don’t wear out, but they interact and rub up against one another in ways the programmers themselves cannot predict. —James Gleick

Slide 10

Slide 10 text

Over-engineering

Slide 11

Slide 11 text

Project needs this:

Slide 12

Slide 12 text

ANTLRParser.new

Slide 13

Slide 13 text

Engineer ships this:

Slide 14

Slide 14 text

def create_parser xml = read_ridiculously_long_xml_file config = hash_from_xml(xml) type = config["type"] case type when "ANTLR" then ANTLRParser.new when "yacc" then YACCParser.new else raise "Unknown parser #{type}" end end

Slide 15

Slide 15 text

Why do we do this?

Slide 16

Slide 16 text

We imagine our software will be around forever

Slide 17

Slide 17 text

Life of an Engineer

Slide 18

Slide 18 text

1. n00b 2. Second-system effect 3. Too clever by half 4. Realism 5. Nirvana

Slide 19

Slide 19 text

1. n00b

Slide 20

Slide 20 text

10 PRINT "HELLO WORLD" 20 END

Slide 21

Slide 21 text

Feature spree

Slide 22

Slide 22 text

GPIBDevice::GPIBDevice(int newAddress) : address(newAddress) { // bunch of options code... }

Slide 23

Slide 23 text

GPIBDevice::GPIBDevice(int newAddress, int newSecondary) : address(newAddress), secondary(newSecondary) { // bunch of options code... }

Slide 24

Slide 24 text

GPIBDevice::GPIBDevice(int newAddress, int newSecondary, double newTimeOut, bool txEoi) : address(newAddress), secondary(newSecondary), timeout(ConvertTimeout(newTimeout)), sendEoi(txEoi) { // bunch of options code... }

Slide 25

Slide 25 text

GPIBDevice::GPIBDevice(int newAddress, int newSecondary, double newTimeOut, bool txEoi, bool sendCr, bool sendLf, bool expectCr, bool expectLf, int bufSize, short newId) : address(newAddress), secondary(newSecondary), sendEoi(txEoi), sendReturn(sendCr), sendLinefeed(sendLf), expectReturn(expectCr), expectLinefeed(expectLf), bufferSize(bufSize), id(newId), pBuffer(NULL), receiveLength(0) { // bunch of options code... }

Slide 26

Slide 26 text

No content

Slide 27

Slide 27 text

The cost [of adding a feature] also includes the addition of an obstacle to future expansion.... You will usually wind up with a codebase that is so fragile that new ideas that should be dead-simple wind up taking longer and longer to work into the tangled existing web. The trick is to pick the features that don’t fight each other. —John Carmack

Slide 28

Slide 28 text

GPIBDevice::GPIBDevice(GPIBOptions options) { // ... }

Slide 29

Slide 29 text

The right abstraction wasn’t obvious at first

Slide 30

Slide 30 text

What happens when...

Slide 31

Slide 31 text

You are asked to build this

Slide 32

Slide 32 text

But you want to build this!

Slide 33

Slide 33 text

No content

Slide 34

Slide 34 text

public class Voltron { public Voltron setType(Type type) {} public Voltron setColor(Color color) {} List getParts() {} public static void main(String[] args) { Voltron v = new Voltron(); v.setColor(YELLOW).setType(LION).render(); } }

Slide 35

Slide 35 text

“Hey, this needs to be more general!”

Slide 36

Slide 36 text

public class Voltron extends Robotron { // ... public static void main(String[] args) { Voltron v = new Voltron(); v.setColor(YELLOW).setType(LION).render(); } }

Slide 37

Slide 37 text

public abstract class Robotron implements Transformable { @Override public Robotron render() { for (Part part : this.getParts()) { part.render(); } return this; } // ... }

Slide 38

Slide 38 text

public interface Transformable extends Renderable { T setType(Type type); T setColor(Color color); List getParts(); }

Slide 39

Slide 39 text

This flexibility wasn’t a project requirement

Slide 40

Slide 40 text

Good frameworks are extracted, not invented. —David Heinemeier Hansson

Slide 41

Slide 41 text

What about one-offs?

Slide 42

Slide 42 text

This is okay

Slide 43

Slide 43 text

You look back at all the projects you’ve ever been on, and you can’t think of a single piece of code that wasn’t rewritten at least once, maybe half a dozen times, over the years. —Steve Yegge, http://bit.ly/yegge-mystery-3d

Slide 44

Slide 44 text

One-offs teach us to abstract

Slide 45

Slide 45 text

class BrewerTest : public CoffeeTest { public: bool testBrewing() { brewer.brew(); COFFEE_ASSERT(brewer.brewed()); } };

Slide 46

Slide 46 text

class BrewerTest : public CoffeeTest { public: bool testBrewing() { brewer.brew(); COFFEE_ASSERT(brewer.brewed()); } };

Slide 47

Slide 47 text

class BrewerTest : public Test { public: bool testBrewing() { brewer.brew(); ASSERT(brewer.brewed()); } };

Slide 48

Slide 48 text

Don’t be afraid to use this!

Slide 49

Slide 49 text

Or this!

Slide 50

Slide 50 text

Or this!

Slide 51

Slide 51 text

There is one ongoing project, even in one-offs:

Slide 52

Slide 52 text

YOU

Slide 53

Slide 53 text

No content

Slide 54

Slide 54 text

2. Second-system effect

Slide 55

Slide 55 text

class CHello : public CHelloBase { public: IPFIX(CLSID_CHello); CHello(IUnknown *pUnk); ~CHello(); HRESULT __stdcall PrintSz(LPWSTR pwszString); private: static int cObjRef; };

Slide 56

Slide 56 text

How engineers measure our self-worth

Slide 57

Slide 57 text

?

Slide 58

Slide 58 text

Ability to tackle complexity

Slide 59

Slide 59 text

So we invent extra complexity

Slide 60

Slide 60 text

class Adder def add(a, b) space = \ @antlr_parser.curr_node.space_needed do_stuff_to(a, space) do_stuff_to(b, space) result = \ @antlr_parser.curr_node.mkspace(space) result.fill_with(a + b) end end

Slide 61

Slide 61 text

class Adder def add(a, b) space = \ @antlr_parser.curr_node.space_needed do_stuff_to(a, space) do_stuff_to(b, space) result = \ @antlr_parser.curr_node.mkspace(space) result.fill_with(a + b) end end

Slide 62

Slide 62 text

“I can handle it”

Slide 63

Slide 63 text

Anticipate the WTF? factor

Slide 64

Slide 64 text

“Just because I can... should I?”

Slide 65

Slide 65 text

def create_parser xml = read_ridiculously_long_xml_file config = hash_from_xml(xml) type = config["type"] case type when "ANTLR" then ANTLRParser.new when "yacc" then YACCParser.new else raise "Unknown parser #{type}" end end

Slide 66

Slide 66 text

“Would I want to support this?”

Slide 67

Slide 67 text

Because you’re going to!

Slide 68

Slide 68 text

Have the courage to decide provisionally

Slide 69

Slide 69 text

3. Too clever by half

Slide 70

Slide 70 text

#include #define S "Hello, World\n" main(){exit(printf(S) == strlen(S) ? 0 : 1);}

Slide 71

Slide 71 text

No content

Slide 72

Slide 72 text

Over-applying DRY

Slide 73

Slide 73 text

it "converts to Fahrenheit" do zero_c.fahrenheit.must_equal(32.0) end it "converts to Kelvin" do zero_c.kelvin.must_equal(273.0) end

Slide 74

Slide 74 text

it "converts to Fahrenheit" do zero_c.fahrenheit.must_equal(32.0) end it "converts to Kelvin" do zero_c.kelvin.must_equal(273.0) end

Slide 75

Slide 75 text

No content

Slide 76

Slide 76 text

[["Fahrenheit", 32.0], ["Kelvin", 273.0]].each do |units, expected| it "converts to #{units}" do method = units.downcase.to_sym actual = celsius.send(method) actual.must_equal(expected) end end

Slide 77

Slide 77 text

No content

Slide 78

Slide 78 text

And if you really have a lot of data...

Slide 79

Slide 79 text

Celsius Fahrenheit Kelvin 0 32 273 100 212 373

Slide 80

Slide 80 text

Use a table!

Slide 81

Slide 81 text

| Celsius | Fahrenheit | Kelvin | | 0 | 32 | 273 | | 100 | 212 | 373 |

Slide 82

Slide 82 text

Choose the right idiom for the environment

Slide 83

Slide 83 text

def first_after(day) @table.find { |m| m.day > day } || no_meas end

Slide 84

Slide 84 text

measurement* found = find_if(table, table + size, compose1( // f(g(pair)), where: bind2nd(greater(), day), // f(num) is "num > day?" select1st())); // g(pair) is "pair.first" return found ? *found : no_meas;

Slide 85

Slide 85 text

No content

Slide 86

Slide 86 text

Fear of the for loop

Slide 87

Slide 87 text

for (int i = 0; i < size; ++i) { if (table[i].first > day) { return table[i]; } } return no_meas;

Slide 88

Slide 88 text

4. Realism

Slide 89

Slide 89 text

echo "Hello, world."

Slide 90

Slide 90 text

No content

Slide 91

Slide 91 text

We make discoveries, we autotune

Slide 92

Slide 92 text

A generic probabilistic metaheuristic for the global optimization problem of locating a good approximation to the global optimum of a given function in a large search space. —http://en.wikipedia.org/wiki/Simulated_annealing

Slide 93

Slide 93 text

Interfaces don’t require multiple implementations

Slide 94

Slide 94 text

class WORNStorage { public: void write(const char* k, void* v) { // poke hardware registers } };

Slide 95

Slide 95 text

int answer = 42; storage->write("answer", &answer);

Slide 96

Slide 96 text

class Storable { public: virtual ~Storable() {} virtual void write(const char* k, void* v) = 0; }; class WORNStorage : public Storable { public: virtual void write(const char* k, void* v) { // poke hardware registers } };

Slide 97

Slide 97 text

Testability

Slide 98

Slide 98 text

class FakeStorage : public Storable { public: virtual void write(const char* k, void* v) { // DO NOTHING! } }; FakeStorage fake; someFunctionThatStores(fake);

Slide 99

Slide 99 text

Convenience

Slide 100

Slide 100 text

Storable* getStorableFromSomewhere() { return new WORNStorage(); }

Slide 101

Slide 101 text

Conway’s Law and how to use it to your advantage

Slide 102

Slide 102 text

Organizations which design systems... are constrained to produce designs which are copies of the communication structures of these organizations. —Melvin Conway

Slide 103

Slide 103 text

“We’re screwed. The design is going to be bureaucratic and dysfunctional, just like our team.” The Pessimist:

Slide 104

Slide 104 text

The Opportunist: “Sweet. If we organize the team like so, the design will naturally fall out the way we want it to.”

Slide 105

Slide 105 text

Bad programmers worry about the code. Good programmers worry about data structures and their relationships. —Linus Torvalds

Slide 106

Slide 106 text

Don’t solve organizational problems with technical solutions

Slide 107

Slide 107 text

Know your needs

Slide 108

Slide 108 text

Chances are, someone has already done it

Slide 109

Slide 109 text

If you can get 90 percent of the desired effect for 10 percent of the work, use the simpler solution. —Bob Scheifler and Jim Gettys

Slide 110

Slide 110 text

class Project < ActiveRecord::Base has_many :documents end class Document < ActiveRecord::Base belongs_to :project end

Slide 111

Slide 111 text

get '/' do haml :index end get '/projects/:id' do @project = Project.find params[:id] haml :project end

Slide 112

Slide 112 text

@@index %ul - Project.all.each do |pj| %li %a{:href => "/projects/#{pj.id}"} = pj.name @@project - @project.documents.each do |d| %li= d.title

Slide 113

Slide 113 text

This was not “built to last”

Slide 114

Slide 114 text

...but it did anyway!

Slide 115

Slide 115 text

I LOVE IT WHEN A PLAN COMES TOGETHER

Slide 116

Slide 116 text

5. Nirvana

Slide 117

Slide 117 text

No content

Slide 118

Slide 118 text

All the universal truths:

Slide 119

Slide 119 text

Chicken sexing

Slide 120

Slide 120 text

Chicken sexing The experts had no idea how they had acquired their skills in the first place, or how to transmit those skills to others. —Richard Horsey

Slide 121

Slide 121 text

Chicken sexing We require training, or a great deal of detailed observation, in order for us to notice the relevant features, and thereby allow our unconscious mechanisms to acquire the diagnostic cues —Richard Horsey

Slide 122

Slide 122 text

When to abstract?

Slide 123

Slide 123 text

1. You know in advance e.g., multiple protocols

Slide 124

Slide 124 text

2. You’re writing a library but what % of your projects are libraries?

Slide 125

Slide 125 text

What makes a good abstraction?

Slide 126

Slide 126 text

We control complexity by building abstractions that hide details when appropriate. —Harold Abelson and Gerald Sussman

Slide 127

Slide 127 text

Hadoop

Slide 128

Slide 128 text

public interface Mapper /* ... */ { void map(K1 key, V1 value, OutputCollector output, Reporter reporter) throws IOException; }

Slide 129

Slide 129 text

If you need something once, build it. If you need something twice, pay attention. If you need it a third time, abstract it. —Derick Bailey

Slide 130

Slide 130 text

Fundamentals

Slide 131

Slide 131 text

More! https://github.com/undees/oscon @baqhaidri @undees

Slide 132

Slide 132 text

No content

Slide 133

Slide 133 text

http://i.lvme.me/hvs3fc1.jpg http://www.flickr.com/photos/trialsanderrors/2741360422 http://commons.wikimedia.org/wiki/ File:Colosseum_Colosseo_Coliseum_(8082864097).jpg http://commons.wikimedia.org/wiki/File:Green_field.jpg http://picardfacepalm.com/ http://www.greenandgoldrugby.co.za/plan-a/ http://commons.wikimedia.org/wiki/File:Bitcoin.png http://www.wpclipart.com/computer/keyboard_keys/special_keys/ computer_key_Delete.png.html http://www.flickr.com/photos/72787861@N00/5981733915 http://www.flickr.com/photos/tjfaust/4452365182/ http://www.ariel.com.au/jokes/The_Evolution_of_a_Programmer.html http://origin-ars.els-cdn.com/content/image/1-s2.0-S0360835201000213-gr5.gif http://upload.wikimedia.org/wikipedia/commons/f/fc/Slide_in_Parque.jpg http://img.archiexpo.com/images_ae/photo-g/large-water-slide-for-aquatic- parks-64440-3624991.jpg http://www.break.com/surfacevideo/stuart-smalley-michael-jordan-snl/family- off/ http://twistedsifter.com/2012/04/50-animated-gifs-for-every-situation-ever/