Code Style Guide

Code style #

Tools #

Meltano uses the below tools to enforce consistent code style. Explore the repo to learn of the specific rules and settings of each.

Python:

Flake8 is a python tool that glues together pycodestyle, pyflakes, mccabe, and third-party plugins to check the style and quality of python code, and wemake-python-styleguide is a plugin for Flake8 that offers an extensive set of opinionated rules that encourage clean and correct code.

MyPy is currently only executed as part of the build pipeline in order to avoid overwhelming developers with the complete list of violations. This allows for incremental and iterative improvement without requiring a concerted effort to fix all errors at once.

Javascript:

You may use make lint to automatically lint all your code, or make show_lint if you only want to see what needs to change.

Mantra #

A contributor should know the exact line-of-code to make a change based on convention

In the spirit of GitLab’s “boring solutions” with the above tools and mantra, the codebase is additionally sorted as follows:

Imports #

Javascript imports are sorted using the following pattern:

  1. Code source location: third-party → local (separate each group with a single blank line)
  2. Import scheme: Default imports → Partial imports
  3. Name of the module, alphabetically: ‘lodash’ → ‘vue’

Tip: There should be only 2 blocks of imports with a single blank line between both blocks. The first rule is used to separate both blocks.

import lodash from 'lodash'                  // 1: third-party, 2: default, 3: [l]odash
import Vue from 'vue'                        // 1: third-party, 2: default, 3: [v]ue
import { bar, foo } from 'alib'              // 1: third-party, 2: partial, 3: [a]lib
import { mapAction, mapState } from 'vuex'   // 1: third-party, 2: partial, 3: [v]uex
  // 1 blank line to split import groups
import Widget from '@/component/Widget'      // 1: local, 2: default, 3: @/[c]omponent/Widget
import poller from '@/utils/poller'          // 1: local, 2: default, 3: @/[u]tils/poller
import { Medal } from '@/component/globals'  // 1: local, 2: partial, 3: @/[c]omponent/globals
import { bar, thing } from '@/utils/utils'   // 1: local, 2: partial, 3: @/[u]tils/utils

  // 2 blank lines to split the imports from the code

Python imports are sorted automatically using isort. This is executed as part of the make lint command, as well as during execution of the pre-commit hook.

Definitions #

Object properties and methods are alphabetical where Vuex stores are the exception (defaultState -> getters -> actions -> mutations)

We are looking to automate these rules in https://gitlab.com/meltano/meltano/issues/1609.

When testing your contributions you may need to ensure that your various `__pycache__` directories are removed. This helps ensure that you are running the code you expect to be running.

Feature Flags #

Sometimes it is useful to be able to make preview features available or allow deprecated features to be used for backwards compatibility. To accomplish this, Meltano implements feature flags. For new, deprecated, or experimental features, the relevant code path can be wrapped in a feature flag context.

For example:

from meltano.core.project_settings_service import ProjectSettingsService
from meltano.core.settings_service import EXPERIMENTAL

class ExistingClass:

	def __init__(self):
		self.project = Project.find()
		self.settings_service = ProjectSettingsService(self.project)

	# If this method is called elsewhere in the code and experimental features are
	# not allowed, it will throw an error:
	def experimental_method(self):
		with self.settings_service.feature_flag(EXPERIMENTAL):
			print("Doing experimental behavior...")

	# If this method is called elsewhere, its behavior will vary based on whether
	# the feature flag is set in the project
	# The same pattern can be used to deprecate existing behavior
	def existing_method_with_new_behavior(self):
		with self.settings_service.feature_flag("new_behavior") as new_behavior:
			if new_behavior:
				print("Doing the new behavior...")
			else:
				print("Doing the existing behavior...")