Skip to content

Commit 7fd0b32

Browse files
committed
chore: Add initial project files and configurations
0 parents  commit 7fd0b32

18 files changed

+3191
-0
lines changed

.eslintrc.cjs

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
module.exports = {
2+
root: true,
3+
env: { browser: true, es2020: true },
4+
extends: [
5+
'eslint:recommended',
6+
'plugin:@typescript-eslint/recommended',
7+
'plugin:react-hooks/recommended',
8+
],
9+
ignorePatterns: ['dist', '.eslintrc.cjs'],
10+
parser: '@typescript-eslint/parser',
11+
plugins: ['react-refresh'],
12+
rules: {
13+
'react-refresh/only-export-components': [
14+
'warn',
15+
{ allowConstantExport: true },
16+
],
17+
},
18+
}

.gitignore

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# Logs
2+
logs
3+
*.log
4+
npm-debug.log*
5+
yarn-debug.log*
6+
yarn-error.log*
7+
pnpm-debug.log*
8+
lerna-debug.log*
9+
10+
node_modules
11+
dist
12+
dist-ssr
13+
*.local
14+
15+
# Editor directories and files
16+
.vscode/*
17+
!.vscode/extensions.json
18+
.idea
19+
.DS_Store
20+
*.suo
21+
*.ntvs*
22+
*.njsproj
23+
*.sln
24+
*.sw?

README.md

+165
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
# Defensive Programming & Guard Clause Hell
2+
3+
1. **Defensive Programming**:
4+
- **Definition**: Writing code that anticipates and handles potential errors and edge cases, often through numerous checks and validations.
5+
- **Problem**: While necessary, it can lead to verbose and hard-to-maintain code, commonly referred to as "defensive code bloat".
6+
7+
2. **Guard Clause Hell**:
8+
- **Definition**: Overusing guard clauses or conditionals to check for null or undefined values.
9+
- **Problem**: Can make functions cluttered with checks, leading to decreased readability and increased cognitive load for future maintainers.
10+
11+
3. **Nullish Coalescing and Optional Chaining**:
12+
- **Optional Chaining (`?.`)**: Allows you to safely access nested properties.
13+
```javascript
14+
const value = obj?.property?.nestedProperty;
15+
```
16+
- **Nullish Coalescing (`??`)**: Provides a default value if the left-hand side is null or undefined.
17+
```javascript
18+
const value = obj.property ?? 'default';
19+
```
20+
21+
4. **Data Validation and Schema Libraries**:
22+
- Tools like Joi, Yup, and Zod can be used to validate incoming data structures and ensure required properties are present.
23+
- Example with Joi:
24+
```javascript
25+
const schema = Joi.object({
26+
x: Joi.string().required()
27+
});
28+
const { error, value } = schema.validate(data);
29+
if (error) throw new Error('Validation failed');
30+
```
31+
32+
5. **TypeScript and Type Safety**:
33+
- Using TypeScript can help by providing compile-time checks and ensuring that objects conform to expected shapes.
34+
- Example with TypeScript:
35+
```typescript
36+
interface Data {
37+
x: string;
38+
}
39+
const data: Data = fetchData();
40+
```
41+
42+
### Example Scenario and Solutions
43+
44+
Imagine an API response where you expect an object with a property `x`.
45+
46+
#### Without Proper Handling
47+
```javascript
48+
function processData(response) {
49+
if (response && response.x) {
50+
console.log(response.x);
51+
} else {
52+
console.log('Property x is missing');
53+
}
54+
}
55+
```
56+
57+
#### Using Optional Chaining and Nullish Coalescing
58+
```javascript
59+
function processData(response) {
60+
const x = response?.x ?? 'default value';
61+
console.log(x);
62+
}
63+
```
64+
65+
#### Using a Schema Validation Library (e.g., Joi)
66+
```javascript
67+
const schema = Joi.object({
68+
x: Joi.string().required()
69+
});
70+
71+
function processData(response) {
72+
const { error, value } = schema.validate(response);
73+
if (error) {
74+
console.log('Validation failed');
75+
} else {
76+
console.log(value.x);
77+
}
78+
}
79+
```
80+
81+
#### Using TypeScript for Type Safety
82+
```typescript
83+
interface ApiResponse {
84+
x: string;
85+
}
86+
87+
function processData(response: ApiResponse) {
88+
console.log(response.x);
89+
}
90+
```
91+
92+
93+
### Example Scenario with BigQuery
94+
95+
You have a scenario where BigQuery returns rows as an array or `undefined`, which can lead to unstable code when accessing properties of the response.
96+
97+
### Current Code
98+
99+
```javascript
100+
const [response] = await bigquery.runQuery(query);
101+
return response?.processed_at;
102+
```
103+
104+
### Problems with the Current Code
105+
1. **Unstable Response**: `response` could be `undefined`, leading to potential runtime errors if not checked.
106+
2. **Optional Chaining Overuse**: Repeated use of optional chaining can make the code less readable and harder to maintain.
107+
3. **Lack of Consistent Contract**: The response object structure is not guaranteed, making it less predictable.
108+
109+
### Solution: Abstracting the Function to Ensure Consistent Contract
110+
111+
You can create a wrapper function that ensures the response always has a consistent structure. This function can provide a default object if the response is `undefined`.
112+
113+
### Improved Code
114+
115+
#### Step 1: Create a Wrapper Function
116+
117+
```javascript
118+
async function runQueryWithDefaults(query) {
119+
const [response] = await bigquery.runQuery(query);
120+
return {
121+
processed_at: response?.processed_at ?? null,
122+
// Add other properties as needed with default values
123+
};
124+
}
125+
```
126+
127+
#### Step 2: Use the Wrapper Function
128+
129+
```javascript
130+
const result = await runQueryWithDefaults(query);
131+
return result.processed_at;
132+
```
133+
134+
### Full Example
135+
136+
Let's create a full example to demonstrate this concept:
137+
138+
```javascript
139+
const { BigQuery } = require('@google-cloud/bigquery');
140+
const bigquery = new BigQuery();
141+
142+
async function runQueryWithDefaults(query) {
143+
try {
144+
const [response] = await bigquery.query(query);
145+
return {
146+
processed_at: response?.processed_at ?? null,
147+
// Add other properties as needed with default values
148+
};
149+
} catch (error) {
150+
console.error('Query failed:', error);
151+
return {
152+
processed_at: null,
153+
// Add other properties with default error values
154+
};
155+
}
156+
}
157+
158+
async function main() {
159+
const query = 'SELECT processed_at FROM your_table LIMIT 1';
160+
const result = await runQueryWithDefaults(query);
161+
console.log(result.processed_at);
162+
}
163+
164+
main();
165+
```

index.html

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<!doctype html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8" />
5+
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
6+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
7+
<title>Vite + React + TS</title>
8+
</head>
9+
<body>
10+
<div id="root"></div>
11+
<script type="module" src="/src/main.tsx"></script>
12+
</body>
13+
</html>

0 commit comments

Comments
 (0)