diff --git a/README.md b/README.md
index b8cd27a96..1909e03d4 100644
--- a/README.md
+++ b/README.md
@@ -1,94 +1,157 @@
-# The ops library
+# The `ops` library
-
-The ops library is a Python framework ([`available on PyPI`](https://pypi.org/project/ops/)) for developing
-and testing [Juju](https://juju.is/) charms in a consistent way, using standard Python constructs
-to allow for clean, maintainable, and reusable code.
+The `ops` library is a Python framework for developing and testing Kubernetes and machine [charms](https://juju.is/docs/sdk/charmed-operators). While charms can be written in any language, `ops` defines the latest standard, and charmers are encouraged to use Python with `ops` for all charms. The library is an official component of the Charm SDK, itself a part of [the Juju universe](https://juju.is/).
-A charm is an operator -- business logic encapsulated in a reusable software
-package that automates every aspect of an application's life.
+> - `ops` is [available on PyPI](https://pypi.org/project/ops/).
+> - The latest version of `ops` requires Python 3.8 or above.
-Charms written with ops support Kubernetes using Juju's "sidecar charm"
-pattern, as well as charms that deploy to Linux-based machines and containers.
+||||
+|-|-|- |
+|| [Juju](https://juju.is/docs/juju) | Learn how to quickly deploy, integrate, and manage charms on any cloud with Juju.
_It's as simple as `juju deploy foo`, `juju integrate foo bar`, and so on -- on any cloud._ |
+||||
+|| [Charmhub](https://charmhub.io/) | Sample our existing charms on Charmhub.
_A charm can be a cluster ([OpenStack](https://charmhub.io/openstack-base), [Kubernetes](https://charmhub.io/charmed-kubernetes)), a data platform ([PostgreSQL](https://charmhub.io/postgresql-k8s), [MongoDB](https://charmhub.io/mongodb), etc.), an observability stack ([Canonical Observability Stack](https://charmhub.io/cos-lite)), an MLOps solution ([Kubeflow](https://charmhub.io/kubeflow)), and so much more._ |
+||||
+|:point_right:| [Charm SDK](https://juju.is/docs/sdk) | Write your own charm!
_Juju is written in Go, but our SDK supports easy charm development in Python._ |
-Charms should do one thing and do it well. Each charm drives a single
-application and can be integrated with other charms to deliver a complex
-system. A charm handles creating the application in addition to scaling,
-configuration, optimisation, networking, service mesh, observability, and other
-day-2 operations specific to the application.
+## Give it a try
-The ops library is part of the Charm SDK (the other part being Charmcraft).
-Full developer documentation for the Charm SDK is available at
-https://juju.is/docs/sdk.
+Let's use `ops` to build a Kubernetes charm:
-To learn more about Juju, visit https://juju.is/docs/olm.
+### Set up
+> See [Charm SDK | Set up an Ubuntu `charm-dev` VM with Multipass](https://juju.is/docs/sdk/dev-setup#heading--automatic-set-up-an-ubuntu-charm-dev-vm-with-multipass).
Choose the MicroK8s track.
-## Pure Python
-The framework provides a standardised Python object model that represents the
-application graph, as well as an event-handling mechanism for distributed
-system coordination and communication.
+### Write your charm
-The latest version of ops requires Python 3.8 or above.
+On your Multipass VM, create a charm directory and use Charmcraft to initialise your charm file structure:
-Juju itself is written in Go for efficient concurrency even in large
-deployments. Charms can be written in any language, however, we recommend using
-Python with this framework to make development easier and more standardised.
-All new charms at Canonical are written using it.
+```shell-script
+mkdir ops-example
+cd ops-example
+charmcraft init
+```
+This has created a standard charm directory structure. Poke around.
+
+Things to note:
+- The `metadata.yaml` file shows that what we have is an example charm called `ops-example`, which uses an OCI image resource `httpbin` from `kennethreitz/httpbin`.
-## Getting started
+- The `requirements.txt` file lists the version of `ops` to use.
-A package of operator code is called a charmed operator or simply "charm".
-You'll use [charmcraft](https://juju.is/docs/sdk/install-charmcraft) to
-register your charm name and publish it when you are ready. You can follow one
-of our [charming tutorials](https://juju.is/docs/sdk/tutorials) to get started
-writing your first charm.
+- The `src/charm.py` file imports `ops` and uses `ops` constructs to create a charm class `OpsExampleCharm`, observe Juju events, and pair them to event handlers:
+```python
+import ops
+
+class OpsExampleCharm(ops.CharmBase):
+ """Charm the service."""
+
+ def __init__(self, *args):
+ super().__init__(*args)
+ self.framework.observe(self.on['httpbin'].pebble_ready, self._on_httpbin_pebble_ready)
+ self.framework.observe(self.on.config_changed, self._on_config_changed)
+
+ def _on_httpbin_pebble_ready(self, event: ops.PebbleReadyEvent):
+ """Define and start a workload using the Pebble API.
+
+ Change this example to suit your needs. You'll need to specify the right entrypoint and
+ environment configuration for your specific workload.
+
+ Learn more about interacting with Pebble at at https://juju.is/docs/sdk/pebble.
+ """
+ # Get a reference the container attribute on the PebbleReadyEvent
+ container = event.workload
+ # Add initial Pebble config layer using the Pebble API
+ container.add_layer("httpbin", self._pebble_layer, combine=True)
+ # Make Pebble reevaluate its plan, ensuring any services are started if enabled.
+ container.replan()
+ # Learn more about statuses in the SDK docs:
+ # https://juju.is/docs/sdk/constructs#heading--statuses
+ self.unit.status = ops.ActiveStatus()
+```
-## Testing your charms
+> See more: [`ops.PebbleReadyEvent`](https://ops.readthedocs.io/en/latest/index.html#ops.PebbleReadyEvent)
-The framework provides a testing harness, so you can ensure that your charm
-does the right thing in different scenarios, without having to create
-a full deployment. Our [API documentation](https://ops.readthedocs.io/en/latest/#module-ops.testing)
-has the details, including this example:
+- The `tests/unit/test_charm.py` file imports `ops.testing` and uses it to set up a testing harness:
```python
+import ops.testing
+
class TestCharm(unittest.TestCase):
- def test_foo(self):
- harness = Harness(MyCharm)
- self.addCleanup(harness.cleanup) # always clean up after ourselves
+ def setUp(self):
+ self.harness = ops.testing.Harness(OpsExampleCharm)
+ self.addCleanup(self.harness.cleanup)
+ self.harness.begin()
+
+ def test_httpbin_pebble_ready(self):
+ # Expected plan after Pebble ready with default config
+ expected_plan = {
+ "services": {
+ "httpbin": {
+ "override": "replace",
+ "summary": "httpbin",
+ "command": "gunicorn -b 0.0.0.0:80 httpbin:app -k gevent",
+ "startup": "enabled",
+ "environment": {"GUNICORN_CMD_ARGS": "--log-level info"},
+ }
+ },
+ }
+ # Simulate the container coming up and emission of pebble-ready event
+ self.harness.container_pebble_ready("httpbin")
+ # Get the plan now we've run PebbleReady
+ updated_plan = self.harness.get_container_pebble_plan("httpbin").to_dict()
+ # Check we've got the plan we expected
+ self.assertEqual(expected_plan, updated_plan)
+ # Check the service was started
+ service = self.harness.model.unit.get_container("httpbin").get_service("httpbin")
+ self.assertTrue(service.is_running())
+ # Ensure we set an ActiveStatus with no message
+ self.assertEqual(self.harness.model.unit.status, ops.ActiveStatus())
+```
- # Instantiate the charm and trigger events that Juju would on startup
- harness.begin_with_initial_hooks()
+> See more: [`ops.testing.Harness`](https://ops.readthedocs.io/en/latest/#ops.testing.Harness)
- # Update charm config and trigger config-changed
- harness.update_config({'log_level': 'warn'})
- # Check that charm properly handled config-changed, for example,
- # the charm added the correct Pebble layer
- plan = harness.get_container_pebble_plan('prometheus')
- self.assertIn('--log.level=warn', plan.services['prometheus'].command)
+Explore further, start editing the files, or skip ahead and pack the charm:
+
+```shell-script
+charmcraft pack
+```
+
+If you didn't take any wrong turn or simply left the charm exactly as it was, this has created a file called `ops-example_ubuntu-22.04-amd64.charm` (the architecture bit may be different depending on your system's architecture). Use this name and the resource from the `metadata.yaml` to deploy your example charm to your local MicroK8s cloud:
+
+```shell-script
+juju deploy ./ops-example_ubuntu-22.04-amd64.charm --resource httpbin-image=kennethreitz/httpbin
```
+Congratulations, you’ve just built your first Kubernetes charm using `ops`!
+
+### Clean up
+
+> See [Charm SDK | Clean up](https://juju.is/docs/sdk/dev-setup#heading--automatic-set-up-an-ubuntu-charm-dev-vm-with-multipass).
-## Talk to us
+## Next steps
-If you need help, have ideas, or would just like to chat with us, reach out on
-the Charmhub [Mattermost].
+### Learn more
+- Read our [user documentation](https://juju.is/docs/sdk/ops), which includes other guides showing `ops` in action
+- Dig into the [`ops` API reference](https://ops.readthedocs.io/en/latest/)
-We also pay attention to the Charmhub [Discourse].
+### Chat with us
-And of course you can deep dive into the [API reference].
+Read our [Code of conduct](https://ubuntu.com/community/code-of-conduct) and:
+- Join our chat: [Mattermost](https://chat.charmhub.io/charmhub/channels/ops)
+- Join our forum: [Discourse](https://discourse.charmhub.io/)
+
+### File an issue
-[Discourse]: https://discourse.charmhub.io/
-[API reference]: https://ops.readthedocs.io/
-[Mattermost]: https://chat.charmhub.io/charmhub/channels/charm-dev
+- Report an `ops` bug using [GitHub issues](https://github.com/canonical/operator/issues)
+- Raise a general https://juju.is/docs documentation issue on [GitHub | juju/docs](https://github.com/juju/docs)
+### Make your mark
-## Development of the framework
+- Read our [documentation contributor guidelines](https://discourse.charmhub.io/t/documentation-guidelines-for-contributors/1245) and help improve a doc
+- Read our [codebase contributor guidelines](HACKING.md) and help improve the codebase
+- Write a charm and publish it on [Charmhub](https://charmhub.io/)
-See [HACKING.md](HACKING.md) for details on dev environments, testing, and so
-on.
diff --git a/ops/__init__.py b/ops/__init__.py
index bb900bf1f..17b035244 100644
--- a/ops/__init__.py
+++ b/ops/__init__.py
@@ -12,8 +12,6 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-# NOTE: The text below is also at the top of README.md. Keep in sync!
-
"""The ops library: a Python framework for writing Juju charms.
The ops library is a Python framework (`available on PyPI`_) for developing