What is the first thing you do when you create a new repository?
Before I get into the details, let me demystify the term repository. A repository is just a folder, it is as simple as that. A folder on your computer where you keep your project files. The word “repository” sounds fancy, but strip away the jargon and you are left with something you already know how to create. The magic happens when you add a few configuration files that help tools understand your project - and optionally connect it to Git for version control.
A repository is just a folder, it is as simple as that.
And then you add configuration files to make it a repository.
Whether you are setting up a repository for PowerShell scripts, a blog, sample code, or documentation, the fundamentals are the same. A well-configured repository makes collaboration easier, ensures consistency, and helps AI tools like GitHub Copilot understand your project context. Yet, I often see repositories - even from experienced developers - missing essential configuration files.
Over the years, I have built a consistent repository setup that works well with my preferred tools: Visual Studio Code as my editor, GitHub Copilot for AI-assisted development, and Windows Sandbox for testing scripts in a clean and isolated environment. While my examples use Visual Studio Code and PowerShell, many of these ideas work with any editor, and the underlying principles apply regardless of your tools:
- README.md - The essential documentation file that every repository needs
- Community files - LICENSE, CONTRIBUTING.md, and SECURITY.md
- GitHub Copilot instructions - Custom instructions and modular instruction files that help AI understand your project
- Visual Studio Code configuration - Extensions, workspace settings, tasks, and code snippets
- Git configuration -
.gitignore,.gitattributes, and.editorconfigfor consistency - Code quality tools - Linters and analyzers for your language (PSScriptAnalyzer for PowerShell)
What follows is a template you can adapt for any new project.
Tip
Even if you are just creating a local folder with no immediate plans to share, I highly recommend following these practices from the start. When you set up your folder like a proper repository, you make it effortless to share later - whether that means pushing to GitHub, collaborating with your team, or simply opening the folder on a different machine. These practices are not just about sharing; they help you work more efficiently, maintain consistency, and save time on every project.Why repository setup matters
A well-configured repository provides several benefits:
Consistency. When everyone on your team uses the same settings, extensions, and conventions, code reviews become easier and merge conflicts decrease.
Productivity. Pre-configured tasks and settings eliminate repetitive setup work. Open the project, and everything just works.
AI assistance. With proper Copilot instructions, AI tools understand your project context and provide more relevant suggestions.
Documentation. A good README helps others (and your future self) understand the project purpose, structure, and how to get started.
Testing confidence. Tasks that run scripts in various scenarios help you catch issues before they reach production.
Why opening a folder as a workspace matters
Opening a folder as a workspace is a distinction that matters more than most people realize. In the introduction of the post, I said a repository is just a folder - and it is. But when you open that folder in Visual Studio Code, it becomes what the editor calls a workspace, and that changes everything.
Here is something many people overlook: there is a significant difference between opening a single file in Visual Studio Code and opening an entire folder. When you open just a file, Visual Studio Code works in “file mode” - you can edit the file, but you miss out on most of the features that make Visual Studio Code - and most modern editors - powerful.
When you open a folder (using File > Open Folder or code . from the terminal), Visual Studio Code treats it as a workspace. This unlocks:
- Workspace settings - The
.vscode/settings.jsonfile is loaded automatically. - Recommended extensions - Visual Studio Code prompts you to install extensions from
.vscode/extensions.json. - Tasks - You can run tasks defined in
.vscode/tasks.json. - GitHub Copilot context - Copilot reads your
.github/copilot-instructions.mdand.github/instructions/files to understand your project. - Search across files - You can search and replace across your entire project.
- Source control - Git integration works properly, showing changes and history.
- IntelliSense - Code completion understands your entire project, not just the current file.
This is why all the configuration files we discuss in this post live inside the folder. They only work when you open the folder as a workspace.
With the benefits clear and the workspace concept in mind, let us walk through each file and configuration step by step - starting with the most fundamental file every repository needs.
Creating the README.md file
The README.md file is the front door of your repository. It should be the first file you create and should answer the essential questions: What is this project? How do I use it? How do I contribute?
Here is a template that works for most repositories (this example is tailored for PowerShell, but adapt it to your project type):
# Project Name
Brief description of what this repository contains and its purpose.
## Overview
More detailed explanation of the project, its goals, and what problems it solves.
## Prerequisites
- PowerShell 5.1 or PowerShell 7.x
- Visual Studio Code with PowerShell extension
- Required modules (if any)
## Repository Structure
```text
📂 Your Repository/
├─ 📂 .bin/
| └─ 📄 notes.txt # Notes, temporary files or sample data
├─ 📂 .github/
| ├─ 📝 copilot-instructions.md # GitHub Copilot custom instructions
| └─ 📂 instructions/ # Modular Copilot instruction files
| └─ 📝 powershell.instructions.md # Language and style rules
├─ 📂 .vscode/
| ├─ 📋 extensions.json # Recommended Visual Studio Code extensions
| ├─ 📋 settings.json # Workspace settings
| └─ 📋 tasks.json # Task definitions
├─ 📂 solution/
| ├─ 📁 Modules/ # PowerShell modules
| └─ 📂 Scripts/ # PowerShell scripts
| ├─ 📄 helloWorld.ps1 # Script file
| └─ 📝 README.md # README for the scripts folder
└─ 📁 tests/ # Test files
└─ 📝 README.md # README for the tests folder
```
## Getting Started
1. Clone the repository
2. Open in Visual Studio Code
3. Install recommended extensions when prompted
4. Run scripts using the provided Visual Studio Code tasks
## Scripts
| Script | Description |
|--------|-------------|
| `Script1.ps1` | Description of what it does |
| `Script2.ps1` | Description of what it does |
## Testing
Describe how to run tests for your project. Include the command and any prerequisites.
## Contributing
Guidelines for contributing to this repository.
## License
License information.Example README.md template
Tip
Keep your README concise but complete. A good benchmark: if someone clones your repository, they should be able to understand and use it within 5 minutes of reading the README.Creating community files
Community files are optional but recommended, especially if you plan to share your repository publicly or collaborate with others. They establish expectations and provide important information for contributors and users.
| File | When to include |
|---|---|
LICENSE | Essential for public and open source repositories. Tells others how they can use your code. |
CONTRIBUTING.md | Recommended when you expect contributions from others. Explains how to contribute. |
SECURITY.md | Important for projects where security vulnerabilities could have impact. Explains how to report issues responsibly. |
Tip
If you host your repository on GitHub, the Community Profile feature (found under Insights > Community) shows a checklist of recommended files and helps you add missing ones directly from the interface. It is a helpful way to ensure your public repository meets community standards.LICENSE file
The LICENSE file tells others how they can use your code. For PowerShell scripts, the MIT License is popular because it is permissive and simple:
MIT License
Copyright (c) [year] [your name]
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.Example LICENSE file (MIT License)
CONTRIBUTING.md file
The CONTRIBUTING.md file explains how others can contribute to your project. Here is an example:
# Contributing to [Project Name]
Thank you for your interest in contributing!
## How to Contribute
1. Fork the repository
2. Create a feature branch (`git checkout -b feature/amazing-feature`)
3. Make your changes
4. Run tests locally
5. Run your linter (e.g., `Invoke-ScriptAnalyzer -Path ./src -Recurse` for PowerShell)
6. Commit your changes (`git commit -m 'Add amazing feature'`)
7. Push to the branch (`git push origin feature/amazing-feature`)
8. Open a Pull Request
## Coding Standards
- Follow the conventions in `.github/copilot-instructions.md`
- Include documentation for all functions
- Add tests for new functionality
- Follow the project's code quality guidelines
## Reporting Issues
Use GitHub Issues to report bugs or request features. Please include:
- PowerShell version (`$PSVersionTable`)
- Operating system
- Steps to reproduce the issue
- Expected vs actual behaviorExample CONTRIBUTING.md file
SECURITY.md file
The SECURITY.md file provides guidance on how to report security vulnerabilities responsibly:
# Security Policy
## Reporting a Vulnerability
If you discover a security vulnerability in this project, please report it responsibly:
1. **Do not** open a public GitHub issue
2. Email [[email protected]] with details
3. Include steps to reproduce the vulnerability
4. Allow reasonable time for a fix before public disclosure
## Security Best Practices
When using scripts from this repository:
- Review scripts before running them
- Never run scripts with elevated privileges unless necessary
- Do not hardcode credentials - use secure credential storage
- Test in a non-production environment firstExample SECURITY.md file
Setting up GitHub Copilot instructions
GitHub Copilot can be customized with repository-specific instructions that help it understand your project context, coding standards, and preferences. The main entry point is a .github/copilot-instructions.md file - and for larger or more complex projects, you can extend this with modular instruction files in a .github/instructions/ folder. These are some of the most impactful files you can add - they transform generic AI suggestions into project-aware assistance.
Generating instructions with GitHub Copilot
Generating instructions with GitHub Copilot is a practical way to bootstrap a solid starting point. There is no built-in button in Visual Studio Code that scaffolds the .github/copilot-instructions.md file for you - you create it manually (see below) - but you can use GitHub Copilot itself to draft the content. Open the Chat panel in Visual Studio Code (Ctrl + Alt + I) and use a prompt like:
Analyze this entire workspace and generate a complete .github/copilot-instructions.md file.
Your output should:
- Identify the purpose, architecture, and conventions used in this repository
- Infer coding patterns, naming conventions, folder structure, and design principles
- Detect frameworks, libraries, and tools in use
- Identify any implicit rules the codebase follows (style, patterns, do/don't rules)
- Provide guidance for how Copilot should write code, documentation, tests, and comments in this repo
- Include examples of good patterns to follow and bad patterns to avoid
Format the result as a polished, production-ready copilot-instructions.md file with clear sections, headings, and examples.Prompt for generating copilot-instructions.md
GitHub Copilot will examine your project structure, existing configuration files, and code patterns to draft relevant instructions. Use the generated content as a starting point, then customize it to match your specific standards and conventions.
Creating the instructions file
Create the file at .github/copilot-instructions.md. Here is an example for a PowerShell project (adapt the standards section to match your language and conventions):
# Copilot Instructions for [Project Name]
**Purpose**: These instructions guide GitHub Copilot when working in this repository.
---
## Project Overview
This repository contains PowerShell scripts for [describe purpose].
Target environment: [Windows 10/11, Microsoft Intune, etc.]
## File Standards
- **Encoding**: UTF-8 for all files
- **Line endings**: LF (`\n`) - never CRLF
- **Final newline**: Always insert final newline
- **Trailing whitespace**: Trim trailing whitespace
## PowerShell Standards
### Script Header Comments
All scripts must include header comments:
```powershell
# Description: Brief explanation of what the script does
# Elevation: Administrator | User | SYSTEM - Reason why this level is required
```
### Coding Conventions
- Use approved verbs for function names (Get, Set, New, Remove, etc.)
- Use PascalCase for function names and parameters
- Use $camelCase for local variables
- Always use `[CmdletBinding()]` for advanced functions
- Include comment-based help for all functions
- Use full cmdlet names, not aliases (e.g., `Where-Object` not `?`)
### Constrained Language Mode Compatibility
Scripts may run in Constrained Language Mode (CLM). Follow these rules:
- Avoid `Add-Type` with inline C# code
- Do not use `[scriptblock]::Create()`
- Avoid `New-Object -ComObject`
- Use cmdlets instead of .NET methods where possible
- Test scripts with `$ExecutionContext.SessionState.LanguageMode = 'ConstrainedLanguage'`
## Testing Requirements
- All code should have corresponding tests
- Follow consistent test file naming conventions
- Tests should cover success paths and error handling
## Security Considerations
- Never hardcode credentials or secrets
- Use `SecureString` for sensitive data
- Validate all input parameters
- Follow the principle of least privilege for elevation requirements.github/copilot-instructions.md example
How Copilot uses these instructions
When you work in this repository, GitHub Copilot will:
- Follow your coding conventions in suggestions
- Include the required header comments when generating scripts
- Avoid patterns that break Constrained Language Mode
- Generate tests that match your naming conventions
Note
Copilot instructions are additive to the model’s training. They guide but do not guarantee specific behavior. Clear, specific instructions work better than vague guidelines.Using modular instruction files
For larger repositories or teams with multiple areas of responsibility, the .github/instructions/ folder provides a way to break your Copilot instructions into smaller, topic-specific files. Think of it as a policy pack for Copilot - each file defines rules for a specific domain.
The folder structure looks like this:
📂 Your Repository/
└─📂 .github/
├─📂 instructions/
| ├─ 📝 coding-style.instructions.md
| ├─ 📝 architecture.instructions.md
| ├─ 📝 testing.instructions.md
| ├─ 📝 documentation.instructions.md
| ├─ 📝 commit-messages.instructions.md
| └─ 📝 security.instructions.md
└─ 📝 copilot-instructions.mdExample .github/instructions/ folder structure
Each file focuses on a single topic:
| File | Purpose |
|---|---|
coding-style.instructions.md | Language and framework style rules, naming conventions, formatting |
architecture.instructions.md | High-level design principles, folder structure, separation of concerns |
testing.instructions.md | How tests should be written, naming conventions, patterns |
documentation.instructions.md | Documentation tone, structure, and expectations |
commit-messages.instructions.md | Commit and pull request formatting rules |
security.instructions.md | Secure coding expectations, anti-patterns to avoid |
GitHub Copilot merges these files with the main copilot-instructions.md and uses them as a contextual ruleset whenever you ask for help. This approach has several advantages:
- Reusability - You can share the same instruction modules across multiple repositories
- Clarity - Each file has a clear scope, making it easier to review and maintain
- Selective overrides - You only need to customize the files that differ between projects, keeping the main instructions file clean and high-level
Here is an example of a coding-style.instructions.md file for a PowerShell project:
# Coding Style Guidelines
## General Rules
- Match the language and framework conventions already present in the repository.
- Use consistent naming conventions for files, variables, functions, and classes.
- Follow formatting rules enforced by linters or formatters.
## PowerShell-Specific Rules
- Use approved verbs for function names (`Get`, `Set`, `New`, `Remove`).
- Use PascalCase for function names and parameters.
- Use $camelCase for local variables.
- Use full cmdlet names, not aliases (e.g., `Where-Object` not `?`).
## Examples
Good:
- Clear, descriptive names
- Small, focused functions
Bad:
- Abbreviations without context
- Large functions doing multiple things.github/instructions/coding-style.instructions.md example
You can also use GitHub Copilot to generate the modular files. Extend the prompt from the earlier section to include the instructions folder:
Analyze this entire workspace and generate content for both:
1. .github/copilot-instructions.md
2. The .github/instructions/ folder (modular instruction files)
Suggest a modular breakdown for the .github/instructions/ folder and generate
a short, clear, reusable set of instructions for each file.Prompt for generating modular instruction files
Configuring Visual Studio Code extensions
The .vscode/extensions.json file specifies which extensions Visual Studio Code should recommend when someone opens the repository. This ensures everyone has the essential tools installed.
Creating the extensions file
Create the file at .vscode/extensions.json. The extensions you recommend depend on your project type - here is an example for PowerShell development:
{
"recommendations": [
// PowerShell development
"ms-vscode.powershell",
// AI assistance
"github.copilot",
"github.copilot-chat",
// Code quality
"streetsidesoftware.code-spell-checker",
// Git integration
"eamodio.gitlens"
],
"unwantedRecommendations": []
}.vscode/extensions.json example
When someone opens your repository and does not have these extensions installed, Visual Studio Code will prompt them to install the recommended extensions.
| Extension | Publisher | Purpose |
|---|---|---|
ms-vscode.powershell | Microsoft | PowerShell language support, IntelliSense, debugging |
github.copilot | GitHub | AI code completion and suggestions |
github.copilot-chat | GitHub | AI chat interface for coding assistance |
streetsidesoftware.code-spell-checker | Street Side Software | Spell checking for code and comments |
eamodio.gitlens | GitKraken | Enhanced Git integration and history |
For other project types, replace the language-specific extension with the appropriate one: ms-python.python for Python, esbenp.prettier-vscode for JavaScript/TypeScript, or yzhang.markdown-all-in-one for documentation projects.
Tip
Use theunwantedRecommendations array to specify extensions that should not be recommended. This is useful if an extension causes conflicts or is not compatible with your workflow.Configuring Visual Studio Code workspace settings
The .vscode/settings.json file defines workspace-specific settings that override user settings when working in this repository. This ensures consistent behavior regardless of individual user preferences.
Creating the settings file
Create the file at .vscode/settings.json:
{
// File handling
"files.encoding": "utf8",
"files.eol": "\n",
"files.trimTrailingWhitespace": true,
"files.insertFinalNewline": true,
// Editor behavior
"editor.formatOnSave": true,
"editor.renderWhitespace": "boundary",
"editor.rulers": [120],
// PowerShell-specific settings
"[powershell]": {
"editor.tabSize": 4,
"editor.insertSpaces": true,
"editor.detectIndentation": false,
"editor.wordWrap": "off",
"files.encoding": "utf8",
"files.eol": "\n"
},
// PowerShell extension settings
"powershell.codeFormatting.preset": "OTBS",
"powershell.codeFormatting.alignPropertyValuePairs": true,
"powershell.codeFormatting.useConstantStrings": true,
"powershell.codeFormatting.whitespaceBetweenParameters": true,
"powershell.scriptAnalysis.enable": true,
"powershell.scriptAnalysis.settingsPath": "./PSScriptAnalyzerSettings.psd1",
// Terminal settings
"terminal.integrated.defaultProfile.windows": "PowerShell",
// Files to exclude from explorer
"files.exclude": {
"**/.git": true,
"**/node_modules": true
}
}.vscode/settings.json example
Key settings explained
The key settings in the example above serve the following purposes:
| Setting | Value | Purpose |
|---|---|---|
files.encoding | utf8 | Ensures consistent file encoding |
files.eol | \n | Uses LF line endings for cross-platform compatibility |
files.trimTrailingWhitespace | true | Removes trailing spaces on save |
files.insertFinalNewline | true | Adds newline at end of files |
powershell.codeFormatting.preset | OTBS | One True Brace Style formatting |
powershell.scriptAnalysis.enable | true | Enables PSScriptAnalyzer integration |
Creating Visual Studio Code tasks for script testing
Visual Studio Code tasks allow you to run scripts and commands directly from the editor. For PowerShell development, I create tasks that run scripts in various scenarios to catch environment-specific issues.
Why test in different scenarios?
PowerShell scripts can behave differently depending on:
- Architecture (32-bit vs 64-bit) - Some scripts interact with the registry or COM objects that behave differently
- Language mode (Full vs Constrained) - Enterprise environments with Application Control for Business or AppLocker use Constrained Language Mode
- Execution policy - Different policies affect script execution
- PowerShell version - Windows PowerShell 5.1 vs PowerShell 7.x
Creating the tasks file
Create the file at .vscode/tasks.json:
{
"version": "2.0.0",
"tasks": [
// Run current script in PowerShell (64-bit)
{
"label": "PowerShell: Run Script (64-bit)",
"type": "shell",
"command": "powershell.exe",
"args": [
"-NoProfile",
"-ExecutionPolicy", "Bypass",
"-File", "${file}"
],
"problemMatcher": ["$msCompile"],
"group": {
"kind": "build",
"isDefault": true
},
"presentation": {
"reveal": "always",
"panel": "shared",
"clear": true
}
},
// Run current script in PowerShell (32-bit)
{
"label": "PowerShell: Run Script (32-bit)",
"type": "shell",
"command": "${env:SystemRoot}\\SysWOW64\\WindowsPowerShell\\v1.0\\powershell.exe",
"args": [
"-NoProfile",
"-ExecutionPolicy", "Bypass",
"-File", "${file}"
],
"problemMatcher": ["$msCompile"],
"presentation": {
"reveal": "always",
"panel": "shared",
"clear": true
}
},
// Run current script in Constrained Language Mode
{
"label": "PowerShell: Run Script (Constrained Language Mode)",
"type": "shell",
"command": "powershell.exe",
"args": [
"-NoProfile",
"-ExecutionPolicy", "Bypass",
"-Command",
"$ExecutionContext.SessionState.LanguageMode = 'ConstrainedLanguage'; & '${file}'"
],
"problemMatcher": ["$msCompile"],
"presentation": {
"reveal": "always",
"panel": "shared",
"clear": true
}
},
// Run current script in PowerShell 7
{
"label": "PowerShell: Run Script (PowerShell 7)",
"type": "shell",
"command": "pwsh.exe",
"args": [
"-NoProfile",
"-ExecutionPolicy", "Bypass",
"-File", "${file}"
],
"problemMatcher": ["$msCompile"],
"presentation": {
"reveal": "always",
"panel": "shared",
"clear": true
}
},
// Run PSScriptAnalyzer on current file
{
"label": "PSScriptAnalyzer: Analyze Current File",
"type": "shell",
"command": "powershell.exe",
"args": [
"-NoProfile",
"-ExecutionPolicy", "Bypass",
"-Command",
"Invoke-ScriptAnalyzer -Path '${file}' -Recurse -ReportSummary"
],
"problemMatcher": ["$msCompile"],
"presentation": {
"reveal": "always",
"panel": "shared",
"clear": true
}
},
// Run PSScriptAnalyzer on entire repository
{
"label": "PSScriptAnalyzer: Analyze All Scripts",
"type": "shell",
"command": "powershell.exe",
"args": [
"-NoProfile",
"-ExecutionPolicy", "Bypass",
"-Command",
"Invoke-ScriptAnalyzer -Path './src' -Recurse -ReportSummary"
],
"problemMatcher": ["$msCompile"],
"presentation": {
"reveal": "always",
"panel": "shared",
"clear": true
}
}
]
}.vscode/tasks.json example
Running tasks
To run a task, use one of these methods:
- Press
Ctrl + Shift + Bto run the default build task - Press
Ctrl + Shift + Pand type “Run Task” to see all available tasks - Use the Terminal menu:
Terminal>Run Task
| Task | Purpose |
|---|---|
| Run Script (64-bit) | Standard execution in 64-bit PowerShell |
| Run Script (32-bit) | Test 32-bit compatibility (registry, COM) |
| Run Script (Constrained Language Mode) | Test CLM compatibility for enterprise deployments |
| Run Script (PowerShell 7) | Test cross-version compatibility |
| Analyze Current File | Run PSScriptAnalyzer on the open file |
| Analyze All Scripts | Run PSScriptAnalyzer on all scripts in src folder |
Important
The Constrained Language Mode task simulates CLM for testing purposes. However, actual CLM behavior enforced by Application Control for Business or AppLocker may differ slightly. Always test in a real CLM environment before deploying to production.Adding Visual Studio Code code snippets
Visual Studio Code supports workspace-level code snippets - reusable text templates that expand when you type a short prefix. By placing a .code-snippets file in the .vscode folder, you make these snippets available to everyone who opens the workspace. This is a small addition that saves time and ensures consistency for repetitive text patterns.
Creating the snippets file
Create a file with the .code-snippets extension in the .vscode folder. The name before the extension is up to you - for example, .vscode/snippets.code-snippets or .vscode/powershell.code-snippets. You can have multiple .code-snippets files in the same folder if you want to organize them by purpose.
Here is an example with snippets for a PowerShell project:
{
"Script header comment": {
"scope": "powershell",
"prefix": "scriptheader",
"body": [
"# Description: $1",
"# Elevation: ${2|is not required,is required|} - $3"
],
"description": "Insert standard script header comment"
},
"CmdletBinding function": {
"scope": "powershell",
"prefix": "cmdletfunction",
"body": [
"function ${1:Verb-Noun} {",
" [CmdletBinding()]",
" param (",
" [Parameter(Mandatory = \\$true)]",
" [string]\\$${2:ParameterName}",
" )",
"",
" begin {",
" $0",
" }",
"",
" process {",
" }",
"",
" end {",
" }",
"}"
],
"description": "Insert a CmdletBinding function template"
}
}.vscode/snippets.code-snippets example
How snippets work
Each snippet has four properties:
| Property | Purpose |
|---|---|
scope | Limits the snippet to specific languages (e.g., powershell, markdown). Omit to make it available everywhere. |
prefix | The text you type to trigger the snippet in IntelliSense |
body | The content to insert. Use $1, $2 for tab stops, ${1:default} for placeholders with defaults, and ${1|option1,option2|} for choice lists. |
description | A short description shown in IntelliSense |
When you type the prefix in a file that matches the scope, Visual Studio Code shows the snippet in IntelliSense. Select it, and the body is inserted with your cursor placed at the first tab stop. Press Tab to jump between placeholders.
Using snippets
Using snippets in Visual Studio Code is straightforward - there are three ways to trigger them:
- IntelliSense - Start typing the snippet prefix and the snippet appears in the IntelliSense suggestion list alongside other completions. If IntelliSense does not appear automatically, press
Ctrl + Spaceto open it manually. Select the snippet from the list and pressEnterto insert it. - Insert Snippet command - Press
Ctrl + Shift + Pto open the Command Palette, type “Insert Snippet”, and browse all available snippets for the current file type. This is useful when you do not remember the exact prefix. - Tab completion - Type the full snippet prefix and press
Tabto expand it directly, without waiting for IntelliSense. This requires enabling the setting"editor.tabCompletion": "on"in your workspace or user settings.
Once a snippet is inserted, you interact with it using tab stops - the placeholders defined in the snippet body. Press Tab to move forward through the placeholders, Shift + Tab to move backward, and Escape to exit snippet mode. If a placeholder offers choices (defined with the ${1|option1,option2|} syntax), a dropdown appears so you can pick a value.
Tip
Workspace snippets are committed to source control, so the entire team benefits from the same shortcuts. This is especially useful for enforcing patterns like script headers, function templates, or boilerplate text that should be consistent across the project.Creating the .gitignore file
The .gitignore file prevents unnecessary files from being committed to your repository. For PowerShell projects, this includes logs, temporary files, and IDE-specific files.
Create the file at .gitignore:
# Folders
bin/
obj/
# PowerShell
*.log
*.tmp
*_log.txt
# Module output
/output/
/release/
# VS Code
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/extensions.json
!.vscode/*.code-snippets
# OS files
Thumbs.db
ehthumbs.db
Desktop.ini
.DS_Store
# Credential files - NEVER commit these
*.pfx
*.cer
*.pem
credentials.json
secrets.json.gitignore example
Warning
Never commit credential files, certificates, or secrets to your repository. Even if you later remove them, they remain in the Git history. Use .gitignore from the start to prevent accidental commits.
GitHub offers Secret Scanning and Push Protection as additional safety nets - these features can detect and even block commits containing known secret patterns - but prevention through .gitignore is always the first line of defense.
Creating the .gitattributes file
While Visual Studio Code settings handle line endings in your editor, .gitattributes ensures Git itself handles line endings correctly regardless of which tools or editors contributors use.
Create the file at .gitattributes:
# Set default behavior to automatically normalize line endings
* text=auto eol=lf
# PowerShell files
*.ps1 text eol=lf
*.psm1 text eol=lf
*.psd1 text eol=lf
*.ps1xml text eol=lf
# Documentation
*.md text eol=lf
*.txt text eol=lf
# Configuration files
*.json text eol=lf
*.xml text eol=lf
*.yml text eol=lf
*.yaml text eol=lf
# Shell scripts
*.sh text eol=lf
*.bash text eol=lf
# Windows batch files - keep CRLF
*.bat text eol=crlf
*.cmd text eol=crlf
# Binary files
*.exe binary
*.dll binary
*.zip binary
*.png binary
*.jpg binary
*.ico binary.gitattributes example
This ensures that:
- PowerShell scripts always use LF line endings (required for cross-platform compatibility)
- Windows batch files keep CRLF (required for Windows command processor)
- Binary files are not modified by Git
Creating the .editorconfig file
The .editorconfig file provides editor-agnostic configuration that works across Visual Studio Code, Visual Studio, Notepad++, Sublime Text, and many other editors. This is especially useful for contributors who may not use Visual Studio Code.
Create the file at .editorconfig:
# EditorConfig is awesome: https://EditorConfig.org
root = true
# Default settings for all files
[*]
charset = utf-8
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
indent_style = space
indent_size = 4
# PowerShell files
[*.{ps1,psm1,psd1}]
indent_size = 4
# Configuration files
[*.{json,yml,yaml}]
indent_size = 2
# Markdown files
[*.md]
indent_size = 2
trim_trailing_whitespace = false
# Batch files - Windows line endings
[*.{bat,cmd}]
end_of_line = crlf.editorconfig example
Note
Markdown files havetrim_trailing_whitespace = false because trailing spaces can be meaningful in Markdown (two trailing spaces create a line break).Code quality tools
Every language has its own linters and static analyzers. For PowerShell, that is PSScriptAnalyzer. For Python, it might be Pylint or Ruff. For JavaScript, ESLint. The principle is the same: configure your quality tools at the repository level so everyone uses the same rules.
| Language | Popular Linters/Analyzers | Configuration File |
|---|---|---|
| PowerShell | PSScriptAnalyzer | PSScriptAnalyzerSettings.psd1 |
| Python | Pylint, Ruff, Black | pyproject.toml, .pylintrc |
| JavaScript/TypeScript | ESLint, Prettier | .eslintrc.json, .prettierrc |
| Go | golangci-lint | .golangci.yml |
| Markdown | markdownlint | .markdownlint.json |
For PowerShell repositories, I covered PSScriptAnalyzer configuration in detail in my post about validating and improving your PowerShell scripts. The key point is to include a settings file in your repository so all contributors use the same rules.
Complete repository structure
After following this guide, your repository should have a structure similar to this (adapt based on your project type):
📂 Your Repository/
├─ 📂 .bin/
| └─ 📄 notes.txt # Notes, temporary files or sample data
├─ 📂 .github/
| ├─ 📂 instructions/ # Modular Copilot instruction files
| | └─ 📝 coding-style.instructions.md # Language and style rules
| └─ 📝 copilot-instructions.md # GitHub Copilot custom instructions
├─ 📂 .vscode/
| ├─ 📋 extensions.json # Recommended Visual Studio Code extensions
| ├─ 📋 settings.json # Workspace settings
| ├─ 📄 snippets.code-snippets # Workspace code snippets
| └─ 📋 tasks.json # Task definitions
├─ 📂 solution/
| ├─ 📁 Modules/ # PowerShell modules
| └─ 📂 Scripts/ # PowerShell scripts
| ├─ 📄 helloWorld.ps1 # Script file
| └─ 📝 README.md # Read me file for the scripts folder
├─ 📁 tests/ # Test files
├─ 📝 README.md # Repository README
├─ 📄 .editorconfig # Cross-editor configuration
├─ 📄 .gitattributes # Git line ending configuration
├─ 📄 .gitignore # Files to exclude from Git
├─ 📝 CONTRIBUTING.md # Contribution guidelines
├─ 📄 LICENSE # License file
└─ 📝 SECURITY.md # Security policyExample repository structure
Putting it all together
Here is a quick checklist for setting up a new repository:
- Create the repository folder
- Initialize Git repository with
git init - Create
README.mdwith project documentation - Add
LICENSEfile (recommended for public repositories) - Add
CONTRIBUTING.mdwith guidelines (optional) - Add
SECURITY.mdwith security policy (optional) - Create
.github/copilot-instructions.mdwith project context and coding standards - Create
.github/instructions/folder with modular instruction files (optional) - Create
.vscode/extensions.jsonwith recommended extensions - Create
.vscode/settings.jsonwith workspace settings - Create
.vscode/tasks.jsonwith development tasks - Create
.vscode/*.code-snippetswith reusable text templates (optional) - Create
.gitignoreto exclude unnecessary files - Create
.gitattributesfor consistent line endings - Create
.editorconfigfor cross-editor consistency - Configure code quality tools for your language (linter settings file)
- Create folder structure for source code and tests
Tip
Consider creating a template repository on GitHub that you can use to quickly scaffold new projects. GitHub’s template repository feature lets you create new repositories with all these files pre-configured.Optional next step: GitHub Actions
Once your repository is configured with proper settings and code quality tools, consider adding GitHub Actions to automate validation on every push and pull request. This is an optional but powerful addition that catches issues before they reach your main branch.
GitHub Actions configuration varies significantly based on your project type, language, and testing requirements - it deserves its own dedicated setup. For detailed guidance, see the GitHub Actions documentation .
What are your experiences?
How do you set up your repositories? Do you have additional files or configurations that you always include? Whether you work with PowerShell, Python, JavaScript, or something else entirely, I would love to hear about your repository setup practices and any tips you have discovered along the way.
–Jesper
Header image attribution: Image created with help from Adobe Firefly

