Most data job seekers treat GitHub like a filing cabinet. This complete lesson teaches you how to structure repositories, write READMEs that tell a technical story, and present code that signals professional readiness to hiring managers — with realistic examples throughout.

Most data job seekers treat GitHub like a filing cabinet — a place to dump notebooks and scripts so they can say they have "a portfolio." Hiring managers can tell immediately. They open a profile and see a graveyard of repositories with names like project1, untitled-notebook, and test-copy-final-v3. There's no README, no context, no story. They close the tab.
The data professionals who get callbacks have GitHub profiles that function more like a curated showroom. Every repository is deliberate. Every README answers the question a hiring manager is actually asking: Can this person think clearly, work systematically, and communicate what they built and why? Your GitHub profile isn't just a code archive — it's a live demonstration of your professional readiness, and it works for you (or against you) even before a recruiter picks up the phone.
By the end of this lesson, you'll know exactly how to structure your GitHub presence to make a strong impression on data hiring managers. You'll walk away with a repeatable system for organizing repositories, writing READMEs that tell a compelling technical story, and presenting code in a way that signals seniority even if you're early in your career.
What you'll learn:
You should be comfortable with Git basics — committing, pushing, branching — and have at least one or two data projects you've worked on, even if they're messy right now. This lesson is about presentation and structure, not building new skills from scratch. If you're still figuring out what projects to build in the first place, start with Building a Data Portfolio That Gets Interviews and come back here when you have material to work with.
Before you start reorganizing anything, you need to understand the experience from the other side of the table. A hiring manager or technical recruiter looking at your GitHub profile has limited time and very specific things they're scanning for.
Here's a realistic picture of what happens: Someone finds your LinkedIn profile, sees a GitHub link, and clicks it. They have about 90 seconds before they decide whether to keep looking or move on. In that window, they're doing a fast scan:
If you pass that initial scan, they go deeper. They'll look at actual code files, check commit messages, and sometimes look at whether you've contributed to other projects. The entire evaluation is happening through the lens of a single question: Does this person work like a professional, or like a student who figured out how to push to GitHub?
Key insight: Your GitHub profile is not evaluated like a homework submission — correctness alone is not enough. Hiring managers are pattern-matching for professional habits: clear naming, documentation, organized structure, and consistency. These signals are often more important than the complexity of your analysis.
This framing changes what you prioritize. You're not trying to impress anyone with the most sophisticated machine learning model. You're trying to demonstrate that you approach technical work the way a professional team would expect.
Before you build anything new, do a ruthless audit of what's already there. Log into GitHub and look at your profile as a stranger would.
Start with repository naming. Go through every repository and ask: Would someone understand what this is from the name alone? Names like homework3, pandas-practice, data-proj, or untitled are actively hurting you. You don't need to delete them — you can make them private. Any repository that isn't ready to show a hiring manager should be set to private.
Here's a quick decision framework for each repo:
Next, look at your pinned repositories. GitHub lets you pin up to six repositories to the top of your profile. If you haven't pinned anything, GitHub defaults to showing your most recently updated repos — which is almost never the right choice. Go to your profile, click "Customize your pins," and select your strongest 4–6 projects.
The goal is to show range without showing weakness. If you have ten repositories, you don't want a hiring manager randomly landing on your worst one. Pinning is how you control the narrative.
Finally, look at your profile metadata. Fill in:
None of these take more than five minutes, and empty fields read as carelessness.
GitHub lets you create a special repository — named exactly the same as your username — whose README automatically appears at the top of your profile page. This is your landing page, and most data job seekers either don't have one or have one that says something useless like "Hi, I'm John! I love data! 😊"
A strong profile README does four things:
Here's a realistic example of a well-structured profile README:
# Hi, I'm Maria Chen — Data Analyst
I specialize in retail and e-commerce analytics, with a focus on
customer behavior modeling and operational reporting automation.
Currently open to data analyst and analytics engineer roles.
---
## Featured Projects
### 🛒 [Customer Churn Analysis — E-commerce Subscription Dataset](link)
Built a logistic regression model to identify at-risk subscribers
using 18 months of transaction data. Achieved 78% recall on the
holdout set. Full write-up includes business recommendations.
**Stack:** Python, pandas, scikit-learn, Matplotlib
### 📊 [Retail Inventory Optimization Dashboard](link)
End-to-end SQL + Tableau project analyzing stockout rates and
reorder point accuracy for a 500-SKU product catalog simulation.
**Stack:** PostgreSQL, dbt (light), Tableau Public
### 🔄 [Automated Sales Reporting Pipeline](link)
Python pipeline that pulls from a public API, transforms data,
and outputs a formatted Excel report — scheduled via cron.
**Stack:** Python, requests, openpyxl, pandas
---
## Technical Skills
**Languages:** Python, SQL
**Libraries:** pandas, NumPy, scikit-learn, Matplotlib, Seaborn
**Tools:** Tableau, dbt, Git, Jupyter, VS Code
**Databases:** PostgreSQL, BigQuery (learning)
---
## Currently Learning
Building toward analytics engineering — currently working through
dbt fundamentals and Airflow orchestration patterns.
📫 [LinkedIn](link) | 📧 maria.chen.data@email.com
Notice what this README doesn't do: it doesn't list every technology Maria has ever touched, it doesn't include a wall of badge icons (those are almost always noise), and it doesn't include vague mission statements. It's a curated menu that tells a hiring manager exactly where to look first.
Tip: Write your profile README last, after you've built out your project repositories. You'll have better material to link to, and the descriptions will feel more natural once you've done the deeper documentation work.
This is where most people make their biggest mistakes. They push a folder of Jupyter notebooks and call it a day. Let's build a repository structure that signals professional competence.
Here's the target folder structure for a data analysis project:
customer-churn-analysis/
│
├── README.md # The main documentation
├── requirements.txt # Python dependencies
├── .gitignore # Ignore checkpoints, .env files, etc.
│
├── data/
│ ├── raw/ # Original, never-modified data
│ │ └── .gitkeep # Placeholder (raw data often not committed)
│ └── processed/ # Cleaned, transformed data
│ └── .gitkeep
│
├── notebooks/
│ ├── 01_exploratory_analysis.ipynb
│ ├── 02_feature_engineering.ipynb
│ └── 03_modeling_and_evaluation.ipynb
│
├── src/
│ ├── __init__.py
│ ├── data_cleaning.py # Reusable cleaning functions
│ ├── feature_engineering.py
│ └── model_evaluation.py
│
├── outputs/
│ ├── figures/ # Saved charts and plots
│ └── reports/ # Summary outputs, slide decks
│
└── docs/
└── data_dictionary.md # Column definitions, data source info
Let's talk through why each piece matters.
The data/ split between raw/ and processed/ is a signal to any experienced data professional that you understand data lineage. Raw data should be sacred — you never modify it in place. When a hiring manager sees this structure, they recognize the pattern immediately.
Numbered notebooks (01_, 02_, 03_) tell a clear story. Someone can open your repo and immediately understand the analytical sequence without reading anything. Compare that to a folder with five notebooks named analysis, analysis_v2, EDA_final, model, and model_results — which one tells a story?
The src/ folder is what separates "I finished a Jupyter tutorial" from "I write production-ready Python." When you extract reusable logic into .py files with real functions, you're demonstrating that you understand software engineering principles, not just data science. You don't need to go overboard — even two or three well-documented helper functions is meaningful.
requirements.txt is non-negotiable. If someone tries to run your code and it breaks because they don't have the right library versions, you've failed a basic professional test. Generate it with pip freeze > requirements.txt or, better yet, maintain it manually so it only includes what your project actually needs (not every package in your environment).
.gitignore should at minimum exclude:
# Common .gitignore for data projects
.ipynb_checkpoints/
__pycache__/
*.pyc
.env
data/raw/*
*.csv # Often excluded if data is large or sensitive
.DS_Store
Warning: Never commit real data containing personally identifiable information (PII), API keys, or credentials to a public repository. Use
.envfiles for secrets and add them to.gitignore. If you accidentally commit sensitive data, simply deleting it in a later commit is not enough — it stays in the git history. You'll need to usegit filter-branchor BFG Repo Cleaner to fully remove it.
The README is the most important file in any repository, and it's the one most people write last and worst. A strong README is not documentation for yourself — it's a sales document aimed at a technical audience. It needs to answer five questions in roughly this order:
Here's what that looks like in practice. This is a realistic README for a churn analysis project:
# Customer Churn Analysis — E-commerce Subscription Data
## Project Overview
This project analyzes 18 months of subscription data from a
simulated e-commerce platform to identify behavioral patterns
that predict customer churn. The goal: give a retention team
actionable signals 30 days before a subscriber cancels.
**Key result:** A logistic regression model achieved 78% recall
on the holdout set at a 40% precision threshold, enabling
proactive outreach to the highest-risk segment.
---
## Problem Statement
Subscription businesses lose significant revenue to passive
churn — customers who simply stop engaging rather than
actively canceling. Standard RFM analysis flags these customers
too late. This project tests whether behavioral engagement
signals (login frequency, feature usage, support contacts) can
predict churn 30 days earlier than the traditional approach.
---
## Data
- **Source:** Simulated dataset based on publicly available
Telco Customer Churn schema (Kaggle), augmented with
synthetic behavioral event data
- **Size:** 7,043 subscribers, 21 features
- **Target variable:** `churned` (1 = canceled within 30 days)
- **Class balance:** 73% retained, 27% churned
Full data dictionary: [docs/data_dictionary.md](docs/data_dictionary.md)
---
## Methodology
1. **EDA:** Distribution analysis, correlation heatmaps, churn
rate segmented by contract type and tenure
2. **Feature engineering:** Created `avg_sessions_last_30d`,
`support_contacts_ratio`, and `days_since_last_login` from
raw event tables
3. **Modeling:** Compared Logistic Regression, Random Forest,
and XGBoost. Selected Logistic Regression for interpretability
— feature coefficients provide direct business guidance.
4. **Evaluation:** Prioritized recall over precision given the
cost asymmetry (missing a churner is more expensive than a
false-positive outreach)
---
## Results
| Model | Precision | Recall | F1 |
|---------------------|-----------|--------|-------|
| Logistic Regression | 0.41 | 0.78 | 0.54 |
| Random Forest | 0.58 | 0.61 | 0.59 |
| XGBoost | 0.55 | 0.65 | 0.60 |
Top predictors of churn:
- `days_since_last_login` (strongest signal)
- `contract_type = month-to-month`
- `avg_sessions_last_30d` below 3
**Business recommendation:** Flag month-to-month customers
with <3 sessions in the past 30 days and >14 days since last
login for immediate retention outreach.
---
## Repository Structure
customer-churn-analysis/ ├── notebooks/ # EDA, feature engineering, modeling ├── src/ # Reusable Python modules ├── data/ # Raw and processed data (see .gitignore) ├── outputs/figures/ # Saved visualizations └── docs/ # Data dictionary
---
## Setup & Reproduction
```bash
git clone https://github.com/mariachen/customer-churn-analysis
cd customer-churn-analysis
pip install -r requirements.txt
jupyter lab
Open notebooks in numbered order (01 → 03).
Python · pandas · scikit-learn · Matplotlib · Seaborn · Jupyter
This README does something most people miss: it leads with **the result**. Don't bury your findings at the bottom. Hiring managers read the top of a README and either keep going or don't. If your first sentence is "In this project I analyze customer data using machine learning," they've already lost interest. If your first paragraph tells them you achieved 78% recall with a model that enables 30-day-early churn detection, they want to know how.
> **Tip:** Treat the top of your README like an executive summary. Lead with the result, then explain the journey. This is also how you should think about [preparing for take-home data assignments](/articles/preparing-for-the-take-home-data-assignment-how-to-structure-your-analysis-code-and-presentation-to-stand-out) — the framing of your findings matters as much as the analysis itself.
---
## Step 5: Write Code That Demonstrates Professional Standards
Code quality is where technical hiring managers spend most of their evaluation time, and it's where the gap between "looks good on the surface" and "actually ready to work on a team" becomes obvious. You don't need to write perfect code — you need to write code that shows you understand professional habits.
Here are the specific things experienced data professionals look for when reviewing your code.
### Functions Over Repeated Blocks
Bad (copied from many portfolios):
```python
# Notebook cell 1
df['age_bucket'] = pd.cut(df['age'], bins=[0, 25, 40, 60, 100],
labels=['18-25', '26-40', '41-60', '60+'])
df['clv_bucket'] = pd.cut(df['clv'], bins=[0, 100, 500, 1000, 9999],
labels=['low', 'medium', 'high', 'premium'])
# Notebook cell 8 (later, same logic repeated slightly differently)
df_test['age_bucket'] = pd.cut(df_test['age'], bins=[0, 25, 40, 60, 100],
labels=['18-25', '26-40', '41-60', '60+'])
Better (extracted to src/feature_engineering.py):
def create_age_buckets(series: pd.Series) -> pd.Series:
"""
Bin customer ages into standard reporting segments.
Parameters
----------
series : pd.Series
Customer age in years (integer or float).
Returns
-------
pd.Series
Categorical series with labels: '18-25', '26-40', '41-60', '60+'
Notes
-----
Customers under 18 or with null age values will be assigned NaN.
"""
bins = [0, 25, 40, 60, 100]
labels = ['18-25', '26-40', '41-60', '60+']
return pd.cut(series, bins=bins, labels=labels)
The difference is significant. The first approach shows someone who learned from a tutorial. The second shows someone who thinks about reusability, documents their assumptions (null handling), and structures code for a team context.
Variable names like df2, temp, result, or x are red flags. So are comments that just describe what the code literally does:
# BAD: Comment explains what, not why
# Filter the dataframe
df_active = df[df['status'] == 'active']
# GOOD: Comment explains the analytical decision
# Exclude churned customers from the training set — we only want
# to model behavioral patterns while the account is still active.
# Churned records are retained in df_holdout for evaluation.
df_active = df[df['status'] == 'active']
Notebooks are often the first thing a reviewer opens. A clean notebook means:
This last point matters more than most people realize. A chart with no title, no axis labels, and no legend is not a portfolio piece — it's a rough draft. Every visualization in your public notebooks should be publication-ready.
import matplotlib.pyplot as plt
import seaborn as sns
fig, ax = plt.subplots(figsize=(10, 5))
churn_by_contract = (
df.groupby('contract_type')['churned']
.mean()
.reset_index()
.rename(columns={'churned': 'churn_rate'})
)
sns.barplot(
data=churn_by_contract,
x='contract_type',
y='churn_rate',
palette='Blues_r',
ax=ax
)
ax.set_title('Churn Rate by Contract Type', fontsize=14, fontweight='bold')
ax.set_xlabel('Contract Type', fontsize=11)
ax.set_ylabel('Churn Rate (proportion)', fontsize=11)
ax.set_ylim(0, 1)
# Annotate bars with values
for p in ax.patches:
ax.annotate(
f'{p.get_height():.1%}',
(p.get_x() + p.get_width() / 2., p.get_height()),
ha='center', va='bottom', fontsize=10
)
plt.tight_layout()
plt.savefig('../outputs/figures/churn_by_contract.png', dpi=150)
plt.show()
Notice that this code saves the figure to the outputs/figures/ directory. Saving output artifacts is another professional signal — it means someone else can reference your results without having to re-run everything.
Note: If you're building SQL-heavy projects, the same standards apply. Create
.sqlfiles in your repository rather than dumping SQL into Python strings. Add comments above complex CTEs explaining the business logic they implement. If you want to demonstrate SQL depth, this is worth reading alongside what you'd see in a structured interview context — the SQL interview questions resource gives you a sense of the complexity level that impresses reviewers.
Your commit history is visible on GitHub, and experienced reviewers sometimes look at it. A commit history that reads:
update
fix
stuff
final
FINAL FINAL
ok this one works
...tells a very clear story about how you work. A commit history that reads:
Add EDA notebook with distribution analysis for key features
Extract feature engineering logic to src/feature_engineering.py
Add logistic regression baseline with 5-fold cross-validation
Tune decision threshold to optimize recall for churn use case
Add README with results summary and business recommendations
Fix: correct data leakage in train/test split (target encoding)
...tells a completely different story. The second history shows someone who works incrementally, thinks about what they're doing at each step, and even caught and fixed a real methodological error (which is actually impressive, not damning, when documented clearly).
No lesson on GitHub profiles would be complete without talking about what to actually build. The best portfolio has 4–6 projects that collectively demonstrate:
What you want to avoid: five projects that are all the same thing (five classification models with sklearn, five EDA notebooks on Kaggle datasets) with different data. Repetition signals that you found a pattern and repeated it, not that you've built genuine versatility.
If you're early in your journey and building out your home lab environment, the data analyst home lab guide has strong suggestions for project datasets and realistic practice scenarios that make for better portfolio pieces than most Kaggle tutorials.
Key insight: A project that documents a failed approach honestly is more impressive than a project that only shows the successful path. If you tried three models and two of them underperformed, say so. Explain why. That's what real analytical work looks like, and it demonstrates critical thinking that a model accuracy table alone cannot.
Here's a structured exercise to put everything from this lesson into practice. This is designed to be done in a focused weekend.
Hour 1–2: Audit and cleanup
Hour 3–6: Rebuild one repository structure
.gitignore and requirements.txtHour 7–12: Write the README
Hour 13–20: Code quality pass
src/ fileHour 21–24: Profile README
At the end of this exercise, you should have one showcase-quality repository and a professional profile landing page. Then repeat the repository process for your remaining pinned projects over the following weeks.
Mistake: The "dumping ground" repository
You have one enormous repository called data-projects with 12 different subdirectories for 12 different projects. This makes it impossible for a hiring manager to evaluate any single project and signals disorganization. Each significant project deserves its own repository.
Mistake: README written for yourself You write a README that assumes the reader already knows what dataset you used and what problem you're solving. Every README should be readable by someone with zero context. Test yours by sending it to someone outside your field and asking if they understand what the project does.
Mistake: Only showing successful projects If all your projects show clean results and perfect accuracy scores, experienced reviewers get suspicious. Real data work involves dead ends, unexpected findings, and models that don't work as hoped. Show that you can navigate that.
Mistake: No recent activity An activity graph that's completely dark for six months looks like you've stopped working in data. Even when you're not doing major projects, small consistent contributions — fixing a bug in an existing project, adding documentation, improving visualizations — keep your graph active and show continued engagement.
Mistake: Pushing Jupyter notebook outputs without cleaning
Jupyter notebooks contain output cells embedded in the JSON. If you ran df.head() 40 times, all 40 outputs are stored in the file. Before committing, restart the kernel and run all cells in order (Kernel → Restart & Run All), then commit. This ensures the notebook is reproducible and that your outputs are current and intentional.
Mistake: Treating your GitHub profile as finished The professionals who get noticed are the ones whose profiles look actively maintained. Your GitHub should reflect where you are now — if you learned dbt three months ago, you should have a project using dbt by now. Hiring managers looking at a profile that hasn't changed in a year wonder what you've been doing.
Warning: Don't fabricate project outcomes or claim results you didn't achieve. If your model got 62% accuracy, say so and explain what you tried to improve it. Hiring managers who do technical reviews will sometimes try to reproduce your results, and a claimed 94% accuracy that can't be replicated is far worse than an honest 62%.
Your GitHub profile is a live professional signal that works whether you're actively applying or not. Hiring managers, recruiters, and potential collaborators look at it before they meet you. The investment you make in structuring it well compounds over time.
Here's what we covered:
src/ for reusable code) to signal that you understand how real teams workYour GitHub profile doesn't exist in isolation. It works alongside your resume, LinkedIn presence, and the way you present yourself in interviews. Once you've built a solid GitHub presence, the natural next step is making sure the rest of your professional footprint is equally strong — creating a LinkedIn profile that attracts recruiters uses a lot of the same principles: curation, clarity, and evidence of professional thinking.
If you're preparing for the actual interview process, the work you've done here gives you something concrete to discuss. You'll know your projects deeply because you documented them thoroughly — which is exactly the preparation that makes technical interview conversations flow naturally. Knowing how to walk through your work confidently is covered in detail in acing the data analyst technical interview.
The goal isn't a perfect GitHub profile before you start applying. The goal is a GitHub profile that's good enough to open doors, and that keeps getting better as you do.
Landing Your First Data Role