Overview
Following consistent code guidelines ensures maintainability and readability across the Kitsu codebase. These guidelines are enforced through ESLint and code review.
General Principles
Readability First Write code that others can easily understand
Consistency Follow existing patterns in the codebase
Simplicity Prefer simple solutions over clever ones
Performance Be mindful of performance implications
Code Style
Line Length
Keep lines under 80 characters when possible:
const result = someFunction (
parameter1 ,
parameter2 ,
parameter3
)
ESLint Rules
Respect the configured ESLint rules. Run the linter before committing:
Automatically fix issues when possible:
Object Field Ordering
Always order object fields alphabetically:
const task = {
assignees: [],
dueDate: '2024-01-15' ,
id: '123' ,
name: 'Animation' ,
status: 'wip'
}
Vue.js Guidelines
Component Structure
Organize component sections in this order:
< template >
<!-- Template -->
</ template >
< script >
export default {
name: 'ComponentName' ,
components: {} ,
mixins: [] ,
props: {} ,
data () {
return {}
} ,
computed: {} ,
watch: {} ,
created () {} ,
mounted () {} ,
beforeUnmount () {} ,
methods: {}
}
</ script >
< style lang = "scss" scoped >
/* Styles */
</ style >
Component Naming
Use PascalCase for component names:
// Good
export default {
name: 'TaskList'
}
// Avoid
export default {
name: 'task-list'
}
Props Definition
Always define props with type and validation:
props : {
task : {
type : Object ,
required : true
},
editable : {
type : Boolean ,
default : false
},
maxItems : {
type : Number ,
default : 10 ,
validator : value => value > 0
}
}
Template Syntax
Use shorthand directives:
< button @ click = " handleClick " : disabled = " isLoading " >
{{ buttonText }}
</ button >
Vuex Guidelines
State Management
Use Vuex state only when necessary. Vuex creates performance overhead - prefer local component state when possible.
Use Vuex when:
State is shared across multiple components
State persists across route changes
Real-time updates affect multiple views
Use local state when:
State is only used in one component
State is temporary (e.g., modal visibility)
Performance is critical
Always Use Actions for API Calls
// In Vuex action
actions : {
async loadTasks ({ commit }, productionId ) {
const tasks = await tasksApi . getTasks ({ productionId })
commit ( LOAD_TASKS_END , tasks )
return tasks
}
}
// In component
methods : {
... mapActions ([ 'loadTasks' ]),
async mounted () {
await this . loadTasks ( this . productionId )
}
}
Mutation Types
Use constants for mutation types:
// mutation-types.js
export const LOAD_TASKS_END = 'LOAD_TASKS_END'
export const UPDATE_TASK = 'UPDATE_TASK'
// In module
import { LOAD_TASKS_END , UPDATE_TASK } from '@/store/mutation-types'
const mutations = {
[ LOAD_TASKS_END ]( state , tasks ) {
state . tasks = tasks
},
[ UPDATE_TASK ]( state , task ) {
const index = state . tasks . findIndex ( t => t . id === task . id )
if ( index >= 0 ) {
state . tasks [ index ] = task
}
}
}
Getters for Derived State
Don’t store computed values in state:
const getters = {
tasks : state => Array . from ( state . taskMap . values ()),
openTasks : ( state , getters ) => {
return getters . tasks . filter ( t => t . status !== 'done' )
},
taskById : state => id => state . taskMap . get ( id )
}
JavaScript Guidelines
Use Modern ES6+ Syntax
// Arrow functions
const double = x => x * 2
// Destructuring
const { name , status } = task
// Template literals
const message = `Task ${ name } is ${ status } `
// Spread operator
const newTask = { ... task , status: 'done' }
// Async/await
async function loadData () {
const data = await api . getData ()
return data
}
Null Safety
Use optional chaining and nullish coalescing:
// Optional chaining
const assigneeName = task ?. assignees ?.[ 0 ]?. name
// Nullish coalescing
const status = task . status ?? 'unknown'
// Default parameters
function greet ( name = 'Guest' ) {
return `Hello, ${ name } `
}
Array Methods
Prefer declarative array methods:
const openTasks = tasks . filter ( t => t . status !== 'done' )
const taskNames = tasks . map ( t => t . name )
const hasWipTasks = tasks . some ( t => t . status === 'wip' )
const total = tasks . reduce (( sum , t ) => sum + t . duration , 0 )
Minimize Watchers
Prefer computed properties over watchers:
computed : {
fullName () {
return ` ${ this . firstName } ${ this . lastName } `
}
}
Use v-show vs v-if Wisely
<!-- Use v-if for rarely toggled content -->
< modal v-if = " isModalOpen " >
<expensive-component />
</ modal >
<!-- Use v-show for frequently toggled content -->
< div v-show = " isVisible " >
<simple-content />
</ div >
Lazy Load Components
For large components, use dynamic imports:
const HeavyComponent = () => import ( '@/components/HeavyComponent.vue' )
export default {
components: {
HeavyComponent
}
}
Testing Guidelines
Write tests for:
Vuex getters, actions, and mutations
Utility functions
Complex component logic
import { describe , it , expect } from 'vitest'
import { sortByName } from '@/lib/sorting'
describe ( 'sortByName' , () => {
it ( 'sorts items alphabetically by name' , () => {
const items = [
{ name: 'Charlie' },
{ name: 'Alice' },
{ name: 'Bob' }
]
const sorted = sortByName ( items )
expect ( sorted [ 0 ]. name ). toBe ( 'Alice' )
expect ( sorted [ 2 ]. name ). toBe ( 'Charlie' )
})
})
JSDoc for Complex Functions
/**
* Calculates the weighted average of task durations
* @param {Array<Object>} tasks - Array of task objects
* @param {string} weightField - Field name to use for weighting
* @returns {number} Weighted average duration
*/
function calculateWeightedAverage ( tasks , weightField ) {
// Implementation
}
Git Commit Messages
Write clear, descriptive commit messages:
Fix: Correct task sorting when status is null
Feature: Add batch export for assets
Refactor: Simplify task comment rendering
Docs: Update API integration guide
Accessibility
Ensure UI is accessible:
<!-- Use semantic HTML -->
< button @ click = " submit " > Submit </ button >
<!-- Not <div @click="submit">Submit</div> -->
<!-- Add labels for form inputs -->
< label for = "task-name" > Task Name </ label >
< input id = "task-name" v-model = " taskName " />
<!-- Add alt text for images -->
< img : src = " thumbnail " : alt = " task . name " />
<!-- Use ARIA attributes when needed -->
< div role = "dialog" aria-labelledby = "modal-title" >
<h2 id="modal-title">{{ title }}</h2>
</ div >
Code Review Checklist
Before submitting a PR, verify:
Linting
npm run lint passes without errors
Tests
npm run test passes, new tests added if needed
Code Style
Follows all guidelines in this document
Performance
No obvious performance issues introduced
Documentation
Complex logic is commented, README updated if needed
Contributing Guide Ready to contribute? Read the full contributing guide