1. Simplicity
- E.g. Pendulum's README starts with sample code.
- E.g. urllib2 has lots of boilerplate code to do an HTTP request compared to Requests library.
- E.g. JavaScript
history.pushState
has bad default argument order:state, title, URL
. Most API clients just want to add a URL to the history and they're forced to specify state and title.
- E.g. JavaScript
- E.g. In Scrapy 1.2, the send method accepts a "to" parameter as a list of strings. If the client passes a single string, the method will iterate over the string, trying to send emails to each character of it. Scrapy 1.3 fixed this by accepting both a single string and a list of strings.
- E.g. a function that only accepts file-like objects will force clients to use
StringIO
if they want to pass strings.
- E.g. a function that only accepts file-like objects will force clients to use
- "If a feature has a high astonishment factor, it may be necessary to redesign it".
- E.g. to make a simple Celery task, devs don't need to worry much about task queues, workers, message brokers, serialization, etc. They just need to use the
@app.task
decorator. The API is focused on the task definition, not on the tasks inner-workings.
- E.g. to make a simple Celery task, devs don't need to worry much about task queues, workers, message brokers, serialization, etc. They just need to use the
- E.g. RPC is almost naturally bad because it abstracts remote resources as local ones, but those should really be handled differently from local ones.
- E.g. in Python 2,
ConfigParser.get
receives asection
andoption
. This isn't natural in Python sinceget
ofdict
receiveskey
anddefault
value. In Python 3, this was fixed by introducing a dict-like interface toConfigParser
.
- E.g. in Python 2,
2. Consistency
- Follow PEP8, the official style guide for Python code. To guarantee adherence to Naming Conventions and other style constraints, validate the API code with flake8.
- E.g.
datetime.datetime(year, month, day, minute, second, microsecond)
vsdatetime.timedelta(days, seconds, microseconds, milliseconds, minutes, hours, weeks)
.
- E.g.
- E.g. Good:
numbers.sort()
in-place vssorted(numbers)
not-in-place.
- E.g. Good:
3. Flexibility:
- E.g. a class to get/set items from/to a cache should separate the behavior to connect to the cache server into another class.
- E.g.
print_formatted
function should be broken into two:print
andformatted
.
- E.g.
- E.g. on Django REST Framework, the CursorPagination class supported only a fixed
page_size
, because it didn't have theget_page_size
method. This was changed by introducing the method.
- E.g. on Django REST Framework, the CursorPagination class supported only a fixed
- E.g. the API is calling another lower-level one, but it isn't exposing some useful parameters the lower-level API supports.
- E.g. Python's built-in sched.scheduler accepts
timefunc
anddelayfunc
, so the tests don't need to mocktime.monotonic
nortime.sleep
and the clients can change those behaviors.
- E.g. Python's built-in sched.scheduler accepts
- E.g. Beautiful Soup provides the same API for multiple parsers.
- E.g. Celery supports both the
@app.task
decorator and a custom task class that inherits fromcelery.Task
.
- E.g. Celery supports both the
- E.g. Django querysets support
.extra
to combine custom SQL with ORM-generated one. It also supports.raw
, which allow completely raw SQL queries.
- E.g. Django querysets support
- E.g. a database connection error should be the same if the API supports multiple database engines. This helps API clients to change engines without changing much code.
- E.g.
list.sort
acceptskey
as the ranker function to calculate the order.
- E.g.
- E.g. pipelines: python-social-auth pipelines, inheritance: Django class-based views, generators: Scrapy spiders.
4. Safety
- E.g. Don't show all if something isn't set.
fields = None
shouldn't mean all fields.
- E.g. Don't show all if something isn't set.
- E.g. django-admin registry, which supports both register by function or by decorator.