developing for developers

56
Developing for Developers Usability Applied to Programming I’ll talk about development for an open-source project / library / plugin - meaning, for other developers. That usually means you’re not paid, you have no commitment on milestones and you are look for fame/love/hate from your peers. In this case, don’t do the exact opposite as when you are paid... Keep usability in mind and mind the developers who will use your work. I’ll focus on applying usability principles to API design, and illustrate by the work I recently done on a symfony plugin

Upload: francois-zaninotto

Post on 15-Nov-2014

10.261 views

Category:

Technology


0 download

DESCRIPTION

Usability Applied to Programming

TRANSCRIPT

Page 1: Developing for Developers

Developing for DevelopersUsability Applied to Programming

I’ll talk about development for an open-source project / library / plugin - meaning, for other developers. That usually means you’re not paid, you have no commitment on milestones and you are look for fame/love/hate from your peers. In this case, don’t do the exact opposite as when you are paid...Keep usability in mind and mind the developers who will use your work.I’ll focus on applying usability principles to API design, and illustrate by the work I recently done on a symfony plugin

Page 2: Developing for Developers

Developing for End-UsersPrototypeUser testingRequirementsSchedulingCodeAPI DocumentationUnit testingUser manualPerformance tweakingPackagingCommunicationSupport

To begin, let’s see what happens when you do not develop directly for developers, but for end-users. That happens if you’re a software editor, or if you’re offering an, internet service for instance.Here is a simplified list of the things you should do to achieve a development for end-users

Page 3: Developing for Developers

Developing for a CustomerPrototypeUser testingRequirementsSchedulingCodeAPI DocumentationUnit testingUser manualPerformance tweakingPackagingCommunicationSupport

Packaging and communication are handled by the customerNow, what happens most of the time when someone develops for developers?

Page 4: Developing for Developers

Developing for DevelopersPrototypeUser testingRequirementsSchedulingCodeAPI DocumentationUnit testingUser manualPerformance tweakingPackagingCommunicationSupport

Most of the time, everything is forgotten. When they’re developing for their peers, developers often omit all but the code This makes it difficult for other developers to use this piece of work.And the most important is: no user manual

Page 5: Developing for Developers

Think “Product”

A library/Plugin deserves the same kind of attention you would give to shaping and creating the next iPod. It may not make you the richest person in the world, but it’s not an excuse to work like an amateur.

Page 6: Developing for Developers

Where to Start1. Tease

2. Work on user manual

3. Write unit tests

4. Write code

5. Release early

6. Communicate

7. Use it yourself

8. Take user feedback into account

9. Go to 1. and start again

Here is a simple process I suggest for developments targeted to developers.It will not take your entire time, but will bring you huge benefitsSince you write Documentation first, I will call this process “Documentation-Driven Development”

Page 7: Developing for Developers

Where to Start1. Tease

2. Work on user manual

3. Write unit tests

4. Write code

5. Release early

6. Communicate

7. Use it yourself

8. Take user feedback into account

9. Go to 1. and start again

Test-DrivenDevelopment

Here is a simple process I suggest for developments targeted to developers.It will not take your entire time, but will bring you huge benefitsSince you write Documentation first, I will call this process “Documentation-Driven Development”

Page 8: Developing for Developers

Where to Start1. Tease

2. Work on user manual

3. Write unit tests

4. Write code

5. Release early

6. Communicate

7. Use it yourself

8. Take user feedback into account

9. Go to 1. and start again

Documentation-DrivenDevelopment

Here is a simple process I suggest for developments targeted to developers.It will not take your entire time, but will bring you huge benefitsSince you write Documentation first, I will call this process “Documentation-Driven Development”

Page 9: Developing for Developers

Doing ItBetter chance to get higher adoption

Better chance to get contributors

Greater code quality

Easier Improvements

Less time spent on support since the documentation is better

More fun

Not Doing ItLess work

Faster?

Good way to have only the true developers using your stuff

Better chance to do something nobody will care about

Pros and ConsFaster? Not in the long runSo let’s illustrate this process throughout a practical example

Page 10: Developing for Developers

DbFinderDbFinder is a usability layer built on top of Propel, that can also be used with Doctrine if you want. It makes retrieving model objects very easy, much easier anyway than what you’re used to do with Propel Criteria objects and Peer classes.I applied the DDD process to the DbFinder development, so it’s a good illustration

Page 11: Developing for Developers

1. Tease

Create expectationsAlso, for an efficient teasing, you need an efficient name, so name your library wisely (not sfPropelImpersonatorPlugin)

Page 12: Developing for Developers

1. Tease

“DbFinder is like jQuery for Propel”

Create expectationsAlso, for an efficient teasing, you need an efficient name, so name your library wisely (not sfPropelImpersonatorPlugin)

Page 13: Developing for Developers

1. Tease

“DbFinder is like jQuery for Propel”

“DbFinder allows for ORM agnostic plugins”

Create expectationsAlso, for an efficient teasing, you need an efficient name, so name your library wisely (not sfPropelImpersonatorPlugin)

Page 14: Developing for Developers

1. Tease

“DbFinder is like jQuery for Propel”

“DbFinder allows for ORM agnostic plugins”

“DbFinder: The ORM isn’t important anymore”

Create expectationsAlso, for an efficient teasing, you need an efficient name, so name your library wisely (not sfPropelImpersonatorPlugin)

Page 15: Developing for Developers

1. Tease

“DbFinder is like jQuery for Propel”

“DbFinder allows for ORM agnostic plugins”

“DbFinder: The ORM isn’t important anymore”

“DbFinder makes you sexy”

Create expectationsAlso, for an efficient teasing, you need an efficient name, so name your library wisely (not sfPropelImpersonatorPlugin)

Page 16: Developing for Developers

1. Tease

“DbFinder is like jQuery for Propel”

“DbFinder allows for ORM agnostic plugins”

“DbFinder: The ORM isn’t important anymore”

“DbFinder makes you sexy”

“Enlarge your Penis with DbFinder!”

Create expectationsAlso, for an efficient teasing, you need an efficient name, so name your library wisely (not sfPropelImpersonatorPlugin)

Page 17: Developing for Developers

2. Work on User ManualKeep usability in mind

Linear, not hypertext

Well written, with no errors and up to date

Must be enough for beginner to intermediate (80% needs)

Let the API documentation explain the advanced

Designed as “a pleasant journey from ignorance to knowledge”

K.I.S.S., D.R.Y.

How do you write a user manual when you have no code? It’s similar to writing requirements, only you go directly to the API.Dry is for the doc, the API as well as for the codeAlso, a few more requirements for the DbFinder user manual specificallyNow, you’ll see through this user manual how to apply usability principles to Development

Page 18: Developing for Developers

2. Work on User ManualKeep usability in mind

Linear, not hypertext

Well written, with no errors and up to date

Must be enough for beginner to intermediate (80% needs)

Let the API documentation explain the advanced

Designed as “a pleasant journey from ignorance to knowledge”

K.I.S.S., D.R.Y.Must be faster to read than Criteria/Peer stuff

Must not require prior knowledge of Criteria/Peer stuff

How do you write a user manual when you have no code? It’s similar to writing requirements, only you go directly to the API.Dry is for the doc, the API as well as for the codeAlso, a few more requirements for the DbFinder user manual specificallyNow, you’ll see through this user manual how to apply usability principles to Development

Page 19: Developing for Developers

Not DDD

$c = new Criteria();$c->add(ArticlePeer::TITLE, '%world', Criteria::LIKE);$c->add(ArticlePeer::IS_PUBLISHED, true);$c->addAscendingOrderByColumn(ArticlePeer::CREATED_AT);$articles = ArticlePeer::doSelectJoinCategory($c);

Here is an example of what you should not do. It is the current Propel query API.What would you need to document to explain how to use Propel’s Peer classes and Criteria stuff?Request a list of Article objects, based on a word in the title and a publication status, and hydrate the related category model object at the same timeBesides, it’s all in the wrong order: you know thet you receive articles at the end...

Page 20: Developing for Developers

Not DDD

$c = new Criteria();$c->add(ArticlePeer::TITLE, '%world', Criteria::LIKE);$c->add(ArticlePeer::IS_PUBLISHED, true);$c->addAscendingOrderByColumn(ArticlePeer::CREATED_AT);$articles = ArticlePeer::doSelectJoinCategory($c);

Here is an example of what you should not do. It is the current Propel query API.What would you need to document to explain how to use Propel’s Peer classes and Criteria stuff?Request a list of Article objects, based on a word in the title and a publication status, and hydrate the related category model object at the same timeBesides, it’s all in the wrong order: you know thet you receive articles at the end...

Page 21: Developing for Developers

Not DDD

$c = new Criteria();$c->add(ArticlePeer::TITLE, '%world', Criteria::LIKE);$c->add(ArticlePeer::IS_PUBLISHED, true);$c->addAscendingOrderByColumn(ArticlePeer::CREATED_AT);$articles = ArticlePeer::doSelectJoinCategory($c);

Here is an example of what you should not do. It is the current Propel query API.What would you need to document to explain how to use Propel’s Peer classes and Criteria stuff?Request a list of Article objects, based on a word in the title and a publication status, and hydrate the related category model object at the same timeBesides, it’s all in the wrong order: you know thet you receive articles at the end...

Page 22: Developing for Developers

Not DDD

$c = new Criteria();$c->add(ArticlePeer::TITLE, '%world', Criteria::LIKE);$c->add(ArticlePeer::IS_PUBLISHED, true);$c->addAscendingOrderByColumn(ArticlePeer::CREATED_AT);$articles = ArticlePeer::doSelectJoinCategory($c);

Here is an example of what you should not do. It is the current Propel query API.What would you need to document to explain how to use Propel’s Peer classes and Criteria stuff?Request a list of Article objects, based on a word in the title and a publication status, and hydrate the related category model object at the same timeBesides, it’s all in the wrong order: you know thet you receive articles at the end...

Page 23: Developing for Developers

Not DDD

$c = new Criteria();$c->add(ArticlePeer::TITLE, '%world', Criteria::LIKE);$c->add(ArticlePeer::IS_PUBLISHED, true);$c->addAscendingOrderByColumn(ArticlePeer::CREATED_AT);$articles = ArticlePeer::doSelectJoinCategory($c);

Here is an example of what you should not do. It is the current Propel query API.What would you need to document to explain how to use Propel’s Peer classes and Criteria stuff?Request a list of Article objects, based on a word in the title and a publication status, and hydrate the related category model object at the same timeBesides, it’s all in the wrong order: you know thet you receive articles at the end...

Page 24: Developing for Developers

Not DDD

$c = new Criteria();$c->add(ArticlePeer::TITLE, '%world', Criteria::LIKE);$c->add(ArticlePeer::IS_PUBLISHED, true);$c->addAscendingOrderByColumn(ArticlePeer::CREATED_AT);$articles = ArticlePeer::doSelectJoinCategory($c);

Here is an example of what you should not do. It is the current Propel query API.What would you need to document to explain how to use Propel’s Peer classes and Criteria stuff?Request a list of Article objects, based on a word in the title and a publication status, and hydrate the related category model object at the same timeBesides, it’s all in the wrong order: you know thet you receive articles at the end...

Page 25: Developing for Developers

Not DDD

$c = new Criteria();$c->add(ArticlePeer::TITLE, '%world', Criteria::LIKE);$c->add(ArticlePeer::IS_PUBLISHED, true);$c->addAscendingOrderByColumn(ArticlePeer::CREATED_AT);$articles = ArticlePeer::doSelectJoinCategory($c);

Here is an example of what you should not do. It is the current Propel query API.What would you need to document to explain how to use Propel’s Peer classes and Criteria stuff?Request a list of Article objects, based on a word in the title and a publication status, and hydrate the related category model object at the same timeBesides, it’s all in the wrong order: you know thet you receive articles at the end...

Page 26: Developing for Developers

Rely on Current Knowledge

English

SQL

sfBrowser / lime_test

ActiveRecord in other languages

To document a new lib, and to design its API, you must rely n current developer knowledgeknowledge comes from practice / habiteases learning a great dealdo not reinvent the wheel

Page 27: Developing for Developers

Keep Number of Public Objects and Methods Low

Every new object is something to explain. To make it simple, reduce the number of objects directly accessible to the end user. This doesn’t prevent you from using more objects under the hood, but it reduces short-term memory load.

Page 28: Developing for Developers

Keep Number of Public Objects and Methods Low

DbFinder

Every new object is something to explain. To make it simple, reduce the number of objects directly accessible to the end user. This doesn’t prevent you from using more objects under the hood, but it reduces short-term memory load.

Page 29: Developing for Developers

Keep Names Short and Easy to Remember

from()

where()

orderBy()

with()

find()

Compare with addAscendingOrderByColumn(), doSelectJoinXXX(), ...Also helps coding faster (less code to type)

Page 30: Developing for Developers

Be Consistent

find()

findPk()

findOne()

findByTitle()

findOneByTitle()

CamelCase, verbs/nouns, no different verbs (retrieve/select/fetch/find/execute)Eases memorizationBonus: in similar situations, users “infer” the API thanks to its consistency

Page 31: Developing for Developers

Design Self-Explanatory API

$articles = DbFinder::from('Article')-> where('Title', 'like', '%world')-> where('IsPublished', true)-> orderBy('CreatedAt')-> with('Category')-> find();

Can somebody explain me what this piece of code is doing (preferably, someone who never ever saw DbFinder code) ?Notice how the first thing in this statement is the declaration of what we look for (it’s the last thing in a usual Propel Query).All the code is pretty much self-explanatory and looks familiar to SQL devsSo seeing some real code becomes somehow a manual. Every application using a well-designed API adds up to the amount of available doc (think askeet, snipeet, etc)

Page 32: Developing for Developers

Group Sequences of Actions

$articles = DbFinder::from('Article')-> where('Title', 'like', '%world')-> where('IsPublished', true)-> orderBy('CreatedAt')-> with('Category')-> find();

Provide a beginning and an end (Initialization method, Termination method)cf: if/endif, foreach/endforeach, etc.Helps organizing code into blocks (more readable, easier to identify)in between: filters, in the flow. Code indentation also helps to identify a sequence

Page 33: Developing for Developers

Cover All Use Cases$articleFinder = DbFinder::from('Article');// Finding all Articles where title = 'foo'$articles = $articleFinder-> where('Title', 'foo')-> find();// Finding all Articles where title like 'foo%'$articles = $articleFinder-> where('Title', 'like', 'foo%')-> find();// Finding all Articles where published_at less than time()$articles = $articleFinder-> where('PublishedAt', '<', time())-> find();

The API should not penalize the features, so don’t simplify too much, or you’ll limit what the library can do

Page 34: Developing for Developers

Cover all Use Cases (2)

// Finding all Articles// Where title is ‘Foo’ or ‘Bar’$article = DbFinder::from('Article')-> where('Title', '=', 'Foo', 'cond1')-> where('Title', '=', 'Bar', 'cond2')-> combine(array('cond1', 'cond2'), 'or')-> findOne();

You probably need to list all use cases before starting your API (that’s the professional way)If you don’t cover 80% of the use cases and allow the remaining 20% to be implemented easily, the library won’t be used

Page 35: Developing for Developers

Offer Shorcuts and Proxy Methods

// Finding all Articles$articles = DbFinder::from('Article')->find();// Finding 3 Articles$articles = DbFinder::from('Article')->find(3);// Finding a single Article$article = DbFinder::from('Article')->findOne();// Finding the last Article // And figure out the column to use for sorting)$article = DbFinder::from('Article')->findLast();

Power users need shorter way to do common stuff. This increases the pace of programming, at the risk of bloating the library. Do it wisely.Watch out: TIMTOWDI is wrong

Page 36: Developing for Developers

Offer Clear BenefitOver the Existing

$pager = DbFinder::from('Article')-> where('Author.Nickname', $nickname)-> with('I18n', 'Category')-> paginate($currentPage = 1, $maxResultsPerPage = 10);

Easyness of use is a clear benefit of DbFinder over PropelThat’s even a benefit over Doctrine!

Page 37: Developing for Developers

Offer Clear BenefitOver the Existing (2)

class ArticleFinder extends DbFinder{ protected $class = 'Article'; public function recent() { $this->where('CreatedAt', '<', time() - 86400); return $this; }}

$articles = DbFinder::from('Article')-> recent()-> find();

Better code readability: the finder ‘filters’ results

Page 38: Developing for Developers

Simple != Not Powerful

$article = DbFinder::from('Article')-> where('Title', 'like', 'foo%')-> addOr(ArticlePeer::TITLE, 'bar%', Criteria::LIKE)-> findOne();

Simple features appeal newcomersPowerful features make them stayYou must target both to get an increasing user base

Page 39: Developing for Developers

Simple != Not Powerful

$article = DbFinder::from('Article')-> where('Title', 'like', 'foo%')-> addOr(ArticlePeer::TITLE, 'bar%', Criteria::LIKE)-> findOne();

Simple features appeal newcomersPowerful features make them stayYou must target both to get an increasing user base

Page 40: Developing for Developers

Use MagicBut Not Too Much

$article = DbFinder::from('Article')-> whereIsPublished()-> orderByCreatedAt()-> withCategory()-> findOne();

I primarily introduced more magic, but recently made it less proeminent or removed it completelyThe developer must be able to understand how the magic works to avoid acting blindly and making mistakesEclipse should be able to provide code hints

Page 41: Developing for Developers

Use MagicBut Not Too Much

$article = DbFinder::from('Article')-> whereIsPublished()-> orderByCreatedAt()-> withCategory()-> findOne();

I primarily introduced more magic, but recently made it less proeminent or removed it completelyThe developer must be able to understand how the magic works to avoid acting blindly and making mistakesEclipse should be able to provide code hints

Page 42: Developing for Developers

Use Real World Examples

// Retrieving an article and its # of comments// In a single query$article = DbFinder::from('Article')-> join('Comment')-> groupBy('Article.Id')-> withColumn('COUNT(Comment.Id)', 'NbComments')-> findOne();echo $article->getColumn('NbComments');

The user manual could almost be reduced to a set of code samplesAfter all, you’re addressing developers, not literature fansDbFinder README file is 90% code

Page 43: Developing for Developers

Borrow Ideas

jQuery

Doctrine

Rails has_finder

SQLAlchemy

You’re not the smartest person in the world

Page 44: Developing for Developers

3. Write Unit Tests

4. Write Code

You know all that stuff. That’s your everyday job, that’s what you do best.Your unit tests can (should) use the same examples as your user manualSo they’re already half-written

Page 45: Developing for Developers

Handle Errors KindlyHelp Debugging

Unique piece of usability than you can’t really plan when writing the user manualTyped exception definitely need to be implemented in DbDinder, to allow to catch them specifically

Page 46: Developing for Developers

Handle Errors KindlyHelp Debugging

Control methods input

Unique piece of usability than you can’t really plan when writing the user manualTyped exception definitely need to be implemented in DbDinder, to allow to catch them specifically

Page 47: Developing for Developers

Handle Errors KindlyHelp Debugging

Control methods input

Provide helpful Exceptions

Unique piece of usability than you can’t really plan when writing the user manualTyped exception definitely need to be implemented in DbDinder, to allow to catch them specifically

Page 48: Developing for Developers

Handle Errors KindlyHelp Debugging

Control methods input

Provide helpful Exceptions

Log information

Unique piece of usability than you can’t really plan when writing the user manualTyped exception definitely need to be implemented in DbDinder, to allow to catch them specifically

Page 49: Developing for Developers

Handle Errors KindlyHelp Debugging

Control methods input

Provide helpful Exceptions

Log information

Provide debugging tools

Unique piece of usability than you can’t really plan when writing the user manualTyped exception definitely need to be implemented in DbDinder, to allow to catch them specifically

Page 50: Developing for Developers

Handle Errors KindlyHelp Debugging

Control methods input

Provide helpful Exceptions

Log information

Provide debugging tools

Automate initialization tasks

Unique piece of usability than you can’t really plan when writing the user manualTyped exception definitely need to be implemented in DbDinder, to allow to catch them specifically

Page 51: Developing for Developers

Handle Errors KindlyHelp Debugging

Control methods input

Provide helpful Exceptions

Log information

Provide debugging tools

Automate initialization tasks

Avoid using private methods/properties

Unique piece of usability than you can’t really plan when writing the user manualTyped exception definitely need to be implemented in DbDinder, to allow to catch them specifically

Page 52: Developing for Developers

5. Release Early (Release Often)

March April June July August September

March 27th - 0.1.0 Alpha•Basic proof of concept•Suitable only for communication•Magic

March 1stStart

March 31st - 0.2.0 Beta• Implementation of most major features• Shortcuts and proxy methods

July 7th - 0.3.0 Beta• Advanced features•Complex use cases•Less magic•Refactoring•Bug fixes•Almost usable for test purpose

August 12th - 0.4.0 Beta•Refactoring•API consolidation•Doctrine adapter•Bug fixes

August 28th - 0.9.0 Beta•Refactoring•Bug fixes•Performance enhancements•100% Doctrine Implementation•Suitable for production

All along, 100% user manual for larger adoption and 100% unit test coverage for regression safety

Page 53: Developing for Developers

6. CommunicateBlog Posts

Forums

Mailing-lists

IRC / Tweets

Friends

Word to Mouth

Reply to comments

Screen captures / Screencasts

Showcase apps / Demo

Conferences

Don’t be deceptive

Be enthusiast

Invite others

Page 54: Developing for Developers

7. Use It YouselfTest it for real. You’ll see what’s not necessary and what’s missing, what’s not practical and how you can improve usability. Even better, if you can: have someone else use the lib (=user testing)sfSimpleBlogPlugin uses DbFinder. It is a living user manual and a real-scale test app

Page 55: Developing for Developers

1. Tease

2. Work on user manual

3. Write unit tests

4. Write code

5. Release early

6. Communicate

7. Use it yourself

8. Take user feedback into account

9. Go to 1. and start again

I’m all ears

Page 56: Developing for Developers

Thank you(we’re recruiting)