Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,16 @@
# Python virtual environment
.venv/

# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class

# VS Code settings
.vscode/

# macOS system files
.DS_Store
# Build
build/

Expand Down
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ To demonstrate the value DevRel programs provide we need metrics, measurements,

* [Requirements and Use Cases](https://github.com/DevRel-Foundation/wg-resource-aggregation/discussions/114)

## Using the Metrics Index

- [How to run scripts](./docs/how-to-run-scripts.md)

## About the Metrics Index

This repository is maintained with support of the [Resource and Asset Aggregation Working Group](https://github.com/DevRel-Foundation/wg-resource-aggregation) and Developer Relations Foundation.
Expand All @@ -19,3 +23,4 @@ This repository is maintained with support of the [Resource and Asset Aggregatio
The DevRel Foundation is on a mission to elevate the professional practice of Developer Relations and increase awareness of it as a driver of business value.

Visit https://dev-rel.org/ to learn more.

23 changes: 23 additions & 0 deletions docs/how-to-run-scripts.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@

# Environment Setup

Some metrics in this project include scripts to demonstrate how to collect data.

## Python Scripts

Use a local Python virtual environment for dependencies. To create and activate it:


```sh
python3 -m venv .venv
source .venv/bin/activate
pip3 install -r requirements.txt
```

If you see no prompt change after activation, verify with `which python`—it should point to `.venv/bin/python`.

**Note:** The `.venv` folder is excluded from version control via `.gitignore`. Each developer should create and activate their own local environment.

## Node Scripts

To be determined.
11 changes: 11 additions & 0 deletions metrics/activity/activity.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@

# Activity Metrics

Activity metrics measure team output and effort. This is an excellent place to start a measurement program because activities your team owns and executes on are within your control. It is reasonable and expected to be able to gather analytics and reports from the tools your team depends.

## Content Production

- [Blog Post Publish Efficiency](./blog-post-publish-rate.md)
- [Blog Post Publish Rate](./blog-post-publish-rate.md)


6 changes: 6 additions & 0 deletions metrics/activity/blog-post-publish-rate/benchmarks.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
org, blog url, feed url, start, end, effort %, total blog posts published, authors, blog post publish rate, blog post efficiency rate
DevRel Foundation, https://dev-rel.org/blog, https://dev-rel.org/blog/feed.xml, 2025-08-01, 2025-11-01, 2.5, 4, 3, 0.30, 4.0143




113 changes: 113 additions & 0 deletions metrics/activity/blog-post-publish-rate/blog-post-publish-rate.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@

# Blog Post Publish Rate

The number of blog posts published over a period of time (weekly).

This is an impactful metric because blog posts as an activity can:
- increase brand awareness
- increase discoverability in organic search (inbound growth)
- deliver value to your ideal customer to solve one of their problems (retention)

You may additionally want to capture **Blog Post Publish Efficiency** which is the publish rate relative to the amount of effort expended (weekly).

## Pre-Conditions

A few things will be needed for successful collection of these metrics.

- you need to have a public blog
- you need to have editorial control to publish at will
- normalize values to 40 hours per work week (5 days) for efficiency calculation

## Measurement

You'll need to gather:
- number of blog posts published between two dates
- amount of time spent producing published posts (estimate)

**Blog Post Production Rate**:
```
number of blog posts published inclusive / number of weeks between first and last publish post
```

**Blog Post Production Rate Efficiency**:
```
number of blog posts published inclusive / hours of effort per post
```

### Example

- Duration: 1 week
- Published posts: 2
- Blog Post Publish Rate: 2.00 (2 / 1)
- Author effort: 5 days
- Blog Post Publish Efficiency: 2.00 (2 / (5 / 5))

### Example

- Duration: 4 weeks
- Published Posts: 9
- Blog Post Publish Rate: 2.25 (9 / 4)
- Total effort by authors (2 authors): 30 days
- Blog Post Publish Efficiency: 1.50 (9 / (30 / 5))

## Analysis

You'll want to combine this activity metric with other outcomes. The objective is to increase the blog publish rate while impacting secondary outcome measures.

- blog attention hours - maintaining or increasing attention hours indicates content is relevant for audience
- blog conversion rate - increasing conversion rate indicates clear CTA

You could also look for trends:
- is blog post publish rate increasing over time
- does blog post publish efficiency vary substantially between two sources of content

### Guardrails

You'll want to combine this activity metric with other outcome metrics that ensure you are achieving a desirable outcome. It would be easy to write shorter articles, low quality articles, or take other short-cuts to increase blog post publish rate at the expense of these desired outcomes.

Good guardrail metrics to compensate:
- blog attention hours - decreasing trend in attention hours indicates content does not engage audience
- blog conversion rate - decreasing conversion rate indicates CTA is unclear

## Obstacles

### What if I don't have a blog?
Free public blogging platforms exist that many teams can leverage. See the DevRel Foundation Tools Catalog for examples. The best time to start a blog if you don't have one for your program is today.

### What If I don't have a blog feed?
If your blog does not support feeds for automatic data collection, you can manually visit the blog and collect the data. Just scroll through the feed and count the number of posts and number of authors for the time period.

### What if I don't have outcome measures?
Collecting activity measures can still be valuable to optimize your workflows around sourcing content.

### What if I don't have enough content being produced?
Many programs will increase their publish rate by sub-contracting out to freelance writers or starting a community writing program.

### What if my team multi-tasks and isn't always focused on content production?
It is common to make a rough approximation. As long as you apply this consistently you can make adjustments in relative terms. You may be able to manage your team in a way to encourage these dedicated periods of time are available.

For example, you may estimate a team of five spends 20% of their time (1 day per week) on blog content production. This is effectively one week of total effort across the team.

### What if my publish rate dropped by half?
It is not unusual for there to be frequent volatility. When there are new product launches or other activities there are many more topics to produce content on. When teams are engaged with technical support, events, or product launches this can pull attention away from content production for a brief time. The measure is a rate, so extend the period of time to a larger frame to smooth the value out if this becomes a concern. It also gives you framing to share with stakeholders on the impact competing programs may have on your content production deliverables.

### What if other teams contribute content?
It is very common to leverage product, engineering, and other departments in the production of content. The blog post publish rate does not account for whom is creating the content. When calculating blog post publish rate efficiency you would count these other contributors efforts into the cumulative total.

## Automation

Many blogging platforms support RSS or Atom Feeds. In this repository is a python script you can run that will fetch the feed and calculate the number of authors and number of blog posts delivered over a period of time.

```
$ python3 blog-post-publish-rate.py --start 2025-07-01 --end 2025-09-30 --feed https://dev-rel.org/blog/feed.xml
...analyzing 12 weeks
...found 10 articles
blog post publish rate = 0.833
...found 3 authors
...default to 50% time effort
blog post publish efficiency = 0.555

$ python3 blog-post-publish-rate.py --start 2025-07-01 --end 2025-09-30 --effort 45 --feed https://dev-rel.org/blog/feed.xml
blog post publish rate = 0.833
blog post publish efficiency = 1.111
```
116 changes: 116 additions & 0 deletions metrics/activity/blog-post-publish-rate/blog-post-publish-rate.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@

"""
Quick and dirty script to calculate blog post publish rate and efficiency from an RSS/Atom feed.

$ python3 blog-post-publish-rate.py --start 2025-08-01 --end 2025-11-01 --feed https://dev-rel.org/blog/feed.xml --effort 2.5
Articles: 4
Authors: 3
Days: 93
Weeks: 13.29
Publish Rate (articles/week): 0.30
Publish Efficiency: 4.0143
"""

import sys
import argparse
import requests
import xml.etree.ElementTree as ET
from datetime import datetime

def parse_args():
parser = argparse.ArgumentParser(description="Calculate blog post publish rate and efficiency.")
parser.add_argument('--start', required=True, help='Start date (YYYY-MM-DD)')
parser.add_argument('--end', required=True, help='End date (YYYY-MM-DD)')
parser.add_argument('--feed', required=True, help='Blog feed URL (RSS/Atom)')
parser.add_argument('--effort', type=float, default=100.0, help='Effort percentage (default: 100)')
parser.add_argument('--info', action='store_true', help='Print extra info')
return parser.parse_args()

def fetch_feed(url):
try:
response = requests.get(url)
response.raise_for_status()
return response.text
except Exception as e:
print(f"Error fetching feed: {e}", file=sys.stderr)
sys.exit(1)

def parse_date(date_str):
# Try common date formats
fmts = [
"%Y-%m-%dT%H:%M:%S%z",
"%Y-%m-%dT%H:%M:%S",
"%Y-%m-%d",
"%a, %d %b %Y %H:%M:%S %Z",
"%a, %d %b %Y %H:%M:%S %z",
"%a, %d %b %Y %H:%M:%S GMT",
"%a, %d %b %Y %H:%M:%S"
]
for fmt in fmts:
try:
return datetime.strptime(date_str, fmt)
except Exception:
continue
return None

def parse_feed(feed_xml, start, end):
root = ET.fromstring(feed_xml)
ns = {'atom': 'http://www.w3.org/2005/Atom', 'rss': 'http://purl.org/rss/1.0/'}
articles = []
authors = set()
# Try Atom first
for entry in root.findall('.//{http://www.w3.org/2005/Atom}entry'):
date_el = entry.find('{http://www.w3.org/2005/Atom}published') or entry.find('{http://www.w3.org/2005/Atom}updated')
author_el = entry.find('{http://www.w3.org/2005/Atom}author/{http://www.w3.org/2005/Atom}name')
if date_el is not None:
pub_date = parse_date(date_el.text)
if pub_date and start <= pub_date <= end:
articles.append(pub_date)
if author_el is not None:
authors.add(author_el.text)
# Try RSS
for item in root.findall('.//item'):
date_el = item.find('pubDate')
# RSS author can be <author> or <dc:creator>
author_el = item.find('author')
if author_el is None:
# Try Dublin Core creator
for child in item:
if child.tag.endswith('creator'):
author_el = child
break
if date_el is not None:
pub_date = parse_date(date_el.text)
if pub_date and start <= pub_date <= end:
articles.append(pub_date)
if author_el is not None and author_el.text:
authors.add(author_el.text.strip())
return articles, authors

def main():
args = parse_args()
start = datetime.strptime(args.start, "%Y-%m-%d")
end = datetime.strptime(args.end, "%Y-%m-%d")
feed_xml = fetch_feed(args.feed)
articles, authors = parse_feed(feed_xml, start, end)
num_articles = len(articles)
num_authors = len(authors)
num_days = (end - start).days + 1
num_weeks = num_days / 7.0
publish_rate = num_articles / num_weeks if num_weeks > 0 else 0
efficiency = publish_rate / (num_authors * (args.effort / 100.0)) if num_authors > 0 and args.effort > 0 else 0
print(f"Articles: {num_articles}")
print(f"Authors: {num_authors}")
print(f"Days: {num_days}")
print(f"Weeks: {num_weeks:.2f}")
print(f"Publish Rate (articles/week): {publish_rate:.2f}")
print(f"Publish Efficiency: {efficiency:.4f}")
if args.info:
print(f"Authors: {', '.join(authors)}")

if __name__ == "__main__":
main()




1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
requests