MERC 5: Explicit arguments

MERC:5
Author:Roberto Rosario
Status:Accepted
Type:Feature
Created:2018-12-30
Last-Modified:2018-12-31

Abstract

This MERC proposes the adoption of a new methodology when performing calls. It seeks to reduce the use of positional arguments in favor of keyword arguments in as many places as possible.

Motivation

As the project grows, legibility of code becomes more important. Keyword argument help document the use of services, classes and functions. Refactors that affect the interface of services are also easier to find and update and fix. Positional argument can cause a call to continue working as long as the datatype of the argument remains the same. Usage of keyword arguments will automatically raise and error that will prevent such situations. Keyword argument further eliminate the relevance of position or the arguments, and the arguments can be sorted alphabetically for easier visual scanning or by semantic significance improving code readability.

Specification

Adoption of this MERC will require an audit of existing calls and the use of the method proposed for new calls. Every call regardless of the type or origin of the source callable will name each argument used. By type it is meant: classes, functions, methods. Origin means: local from the project, from the framework, third party libraries or the standard library.

Backwards Compatibility

No backwards compatibility issues are expected. New errors arising from the use if keyword arguments could be interpreted as existing latent issues that have not been uncovered.

Reference Implementation

Example:

Before:

from mayan.apps.common.classes import Template

Template(
    'menu_main', 'appearance/menu_main.html'
)

After:

from mayan.apps.common.classes import Template

Template(
    name='menu_main', template_name='appearance/menu_main.html'
)

When calls use a mixture or positional and keyword arguments, the keywords arguments can only be found after the positional arguments. Complete use of keyword arguments allow the reposition of arguments for semantic purposes.

Example:

Before:

from django.conf.urls import url

from .views import AboutView, HomeView, RootView

urlpatterns = [
    url(r'^$', RootView.as_view(), name='root'),
    url(r'^home/$', HomeView.as_view(), name='home'),
    url(r'^about/$', AboutView.as_view(), name='about_view'),
]

After:

from django.conf.urls import url

from .views import AboutView, HomeView, RootView

urlpatterns = [
    url(regex=r'^$', name='root', view=RootView.as_view()),
    url(regex=r'^home/$', name='home', view=HomeView.as_view()),
    url(regex=r'^about/$', name='about_view', view=AboutView.as_view()),
]

Keyword arguments should also be used for callables that pass those to others down the line like Django’s reverse function. Any change to the name of the pk URL parameter will raise an exception in this code alerting to any possible incompatible use.

Example:

def get_absolute_url(self):
    return reverse(
        viewname='documents:document_preview', kwargs={'pk': self.pk}
    )

This becomes even more important when multiple URL parameters are used. Since the API documentation is auto generated from the code itself, it would make sense to rename the first URL parameter from pk to document_pk. Such change will cause all address to view resolutions to break forcing their update and allowing all consumers’ interface usage to remain synchronized to the callable’s interface.

url(
    regex=r'^documents/(?P<pk>[0-9]+)/versions/(?P<document_version_pk>[0-9]+)/pages/(?P<document_page_pk>[0-9]+)/image/$',
    name='documentpage-image', view=APIDocumentPageImageView.as_view()
),