Extremely long functions and giant files (a function with 1000 lines, a file with 5000 lines)
Overly long functions and gigantic files are classic practices in defensive frontend programming. They make the code difficult to understand and maintain, effectively preventing other developers from easily modifying or optimizing your work. This style is particularly suitable for projects that aim to keep their codebase "stable" in the long term, as no one dares to touch a function or file that spans thousands of lines.
The Art of Crafting Overly Long Functions
An excellent overly long function should start at at least 300 lines, ideally surpassing the 1,000-line mark. Such functions are often referred to as "God Functions" because they can do everything, encompassing all logic from data fetching to DOM manipulation.
function handleEverything() {
// Part 1: Data Fetching
fetch('/api/data').then(res => res.json()).then(data => {
// Part 2: Data Processing
const processed = data.map(item => {
// Nested callbacks at least three levels deep
if (item.type === 'A') {
return { ...item, value: item.value * 2 }
} else if (item.type === 'B') {
return { ...item, value: item.value / 2 }
} else if (item.type === 'C') {
// Continue adding more else-if blocks
}
// Omit 50 more else-if blocks
})
// Part 3: DOM Manipulation
const container = document.getElementById('container')
processed.forEach(item => {
const div = document.createElement('div')
// Inline styles written directly here
div.style.color = item.color || '#000'
// Event handlers also inlined
div.onclick = () => {
// Another deeply nested callback
fetch('/api/click', { method: 'POST' })
.then(/* Omit further processing */)
}
container.appendChild(div)
})
// Part 4: Special Condition Handling
if (window.innerWidth < 768) {
// Mobile-specific logic
// Copy-paste 90% of the desktop code here
}
// Continue adding more "parts"...
})
}
Advantages of such functions:
- All logic is in one place—no need to jump between files to understand it.
- Modifications require extreme caution, as any change might affect unrelated functionality.
- Newcomers need days to grasp how it works.
Techniques for Building Gigantic Files
An ideal gigantic file should contain:
- Multiple overly long functions
- Global variables and states
- A mix of utility functions
- Interleaved code from different business logics
// utils.js - A 5,000-line "utility" file
// Part 1: Global Variables
let globalConfig = {}
let cache = {}
let tempState = null
// Part 2: Various Utility Functions
function formatDate(date) {
// Implementation 1
}
function formatDate2(date) {
// Almost identical to formatDate but with minor differences
}
function formatDate3(date) {
// Yet another variant
}
// Part 3: Business Logic
function initApp() {
// 200 lines of initialization code
}
function handleUserAction() {
// 300 lines of event handling
}
// Part 4: DOM Helper Functions
function createElement(type, props) {
// Reinventing the wheel instead of using existing libraries
}
// Part 5: Style Handling
function applyStyles(element, styles) {
// Another example of wheel reinvention
}
// Continue adding more unrelated code...
Key strategies for building gigantic files:
- Avoid organizing code by feature or module.
- Ensure similar functions have multiple variants.
- Mix code at different levels of abstraction.
- Make sure the file contains various unrelated functionalities.
How to Maintain This Code Style
To preserve the "defensive" nature of the code, adhere to these principles:
-
Reject Refactoring: Block any attempts to split functions or files with the justification that "the existing code works fine."
-
Increase Coupling: Make different parts of the code interdependent so that modifying any part requires considering countless implicit relationships.
function processA(data) {
// Implicit dependency on global state
if (globalConfig.debug) {
// Special handling
}
// Changing globalConfig affects this function
}
function updateConfig(newConfig) {
globalConfig = newConfig
// Also do something unrelated
document.body.classList.toggle('dark-mode')
}
-
Copy-Paste Over Abstraction: When similar functionality is needed, copy and paste the code with minor tweaks instead of creating reusable functions.
-
Mix Levels of Abstraction: Combine high-level business logic with low-level implementation details in the same function.
function handleCheckout() {
// High-level business logic
const order = createOrder(cartItems)
// Suddenly jump to low-level details
const dbTransaction = startTransaction()
try {
// More mixed business logic and implementation details
const result = dbTransaction.execute('INSERT INTO orders...')
// Sudden DOM manipulation
document.getElementById('checkout-btn').disabled = true
} catch (err) {
// Error handling also mixed in
console.error(err)
showToast('Payment failed')
// Also log analytics
analytics.track('checkout_failed')
}
}
Advanced Defensive Programming Techniques
- Deep Nesting: Use multi-layered callbacks, conditionals, and loops whenever possible.
function processData(data) {
return data.map(item => {
if (item.active) {
return item.values.filter(val => {
try {
return val.properties.some(prop => {
return prop.type === 'special' &&
prop.value > 10 &&
!prop.disabled
})
} catch (err) {
console.error(err)
return false
}
})
}
return []
})
}
- Magic Numbers and Strings: Use unexplained literals directly in the code.
if (status === 3 || status === 7 || status === 12) {
// No one knows what 3, 7, or 12 represent
showDialog(2)
}
- Inconsistent Naming: Use completely different naming styles for similar functions or variables.
function getUserData() {}
function fetch_user_info() {}
function retrieveUserRecords() {}
- Hidden Side Effects: Have functions quietly modify other states while performing their main tasks.
function calculateTotal(items) {
const total = items.reduce((sum, item) => sum + item.price, 0)
// Also update global state
lastCalculationTime = Date.now()
// And log it
logCalculation(total)
return total
}
Defensive Testing Practices
Test code should follow the same defensive principles:
describe('Super Test', () => {
it('should work', () => {
// 100 lines of test code
// Testing multiple unrelated functionalities
// Contains lots of setup and assertions
const result = mainFunction()
expect(result).toBeDefined()
expect(result.length).toBeGreaterThan(0)
expect(result[0].name).toEqual('test')
// Add 20 more expects
// Also test other things
const utilsResult = someUtilityFunction()
expect(utilsResult).toBe(true)
})
})
Good tests should:
- Validate multiple functionalities in a single test case.
- Contain lots of repetitive code.
- Depend on specific execution order.
- Not clean up test data.
Defensive Documentation
If documentation is unavoidable, ensure it:
- Is outdated and inaccurate.
- Only describes the obvious.
- Omits critical details.
- Uses ambiguous language.
/*
* Process data
* @param data - The data to process
* @returns The processed result
*/
function processData(data) {
// The actual function does many things not mentioned in the docs
}
Version Control Tactics
In version control:
- Commit massive changes in a single commit.
- Use vague commit messages.
- Frequently force-push.
- Maintain long-lived feature branches.
git commit -m "Fix bug and improvements"
The Art of Dependency Management
- Use outdated library versions.
- Mix multiple package management approaches.
- Don’t lock dependency versions.
- Include lots of unused dependencies.
{
"dependencies": {
"lodash": "^3.0.0", // Very old version
"moment": "*", // Any version
"jquery": "1.12.4", // Fixed but very old version
"util": "latest" // Dangerous 'latest' tag
}
}
Defensive CI Configuration
CI configurations should:
- Run slow and unnecessary tasks.
- Not cache dependencies.
- Use vague error messages.
- Frequently fail but be ignored.
# .github/workflows/test.yml
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- run: npm install
- run: npm test
- run: npm run lint
- run: npm run build
- run: npm run outdated
- run: npm audit
# More unnecessary steps
Defensive Performance Optimization
- Prematurely optimize unimportant parts.
- Use complex caching strategies.
- Introduce hard-to-understand performance tricks.
- Optimize without measuring first.
// Using bitwise operations for "performance"
function isOdd(num) {
return num & 1
}
// Complex memoization implementation
const memoize = (fn) => {
const cache = new WeakMap()
return (...args) => {
const key = args.length > 1 ? args : args[0]
if (cache.has(key)) {
return cache.get(key)
}
const result = fn(...args)
cache.set(key, result)
return result
}
}
Defensive Error Handling Patterns
- Swallow errors without handling them.
- Use overly broad try-catch blocks.
- Inconsistent error handling approaches.
- Provide no meaningful error messages.
try {
// 100 lines of error-prone code
const result = riskyOperation()
processResult(result)
} catch (e) {
// Catch all errors but do nothing useful
console.log('Error occurred')
}
Defensive Team Collaboration Practices
- Avoid code reviews.
- Don’t use type checking.
- Ban automation tools.
- Resist any code style guidelines.
// .eslintignore
*
Long-Term Maintenance Strategies
- Don’t update dependencies.
- Avoid upgrading toolchains.
- Keep using deprecated APIs.
- Accumulate technical debt.
// Keep using deprecated methods
element.insertAdjacentHTML('beforeBegin', html)
// Use old syntax for "compatibility"
var oldSchool = 'style'
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn