Repository: lace/vg

Issue:
Add a compatibility layer
Since Python modules are installed in a global namespace, breaking changes to APIs can be very difficult to deal with, especially in a toolbelt like this one, since it's intended to be used by whole ecosystems of packages. If a breaking change is introduced here it needs to be updated at every point in the dependency tree, all at once, which is challenging.

I don't want to freeze the API forever, so instead the solution should be to provide a stable compatibility layer for libraries. For example, instead of `import vg`, a library would use `import v1 as vg from vg`. That way, libraries will be inured to future breaking changes, so long as 1.x or later version is used.

Convenience for application code means that `import vg` will keep working, and use the latest version.

It could also be possible to `import next as vg from vg` to provide experimental APIs before they've reached a point of stability.

I'd like to include this before the next major version is released.

Comments:

Patch:
diff --git a/README.md b/README.md
index 37a6e9c..60a4081 100644
--- a/README.md
+++ b/README.md
@@ -13,7 +13,7 @@ A **v**ery **g**ood vector-geometry toolbelt for dealing with 3D points and
 vectors. These are simple [NumPy][] operations made readable, built to scale
 from prototyping to production.
 
-See the complete API reference: https://vgpy.readthedocs.io/en/latest/
+:book: See the complete documentation: https://vgpy.readthedocs.io/en/latest/
 
 [pypi]: https://pypi.org/project/vg/
 [coverage]: https://github.com/lace/vg/blob/master/.coveragerc
@@ -70,14 +70,6 @@ angles = np.arccos(np.clip(cosines, -1.0, 1.0))
 angles = vg.angle(v1s, v2s)
 ```
 
-Features
---------
-
-All functions are optionally vectorized, meaning they accept single inputs and
-stacks of inputs interchangeably. They return The Right Thing – a single
-result or a stack of results – without the need to reshape inputs or outputs.
-With the power of NumPy, the vectorized functions are fast.
-
 Installation
 ------------
 
@@ -100,47 +92,17 @@ projected = vg.scalar_projection(
 ```
 
 
-Design principles
------------------
-
-Linear algebra is useful and it doesn't have to be dificult to use. With the
-power of abstractions, simple operations can be made simple, without poring
-through lecture slides, textbooks, inscrutable Stack Overflow answers, or
-dense NumPy docs. Code that uses linear algebra and geometric transformation
-should be readable like English, without compromising efficiency.
-
-These common operations should be abstracted for a few reasons:
-
-1. If a developer is not programming linalg every day, they might forget the
-   underlying formula. These forms are easier to remember and more easily
-   referenced.
-
-2. These forms tend to be self-documenting in a way that the NumPy forms are
-   not. If a developer is not programming linalg every day, this will again
-   come in handy.
-
-3. These implementations are more robust. They automatically inspect `ndim`
-   on their arguments, so they work equally well if the argument is a vector
-   or a stack of vectors. They are more careful about checking edge cases
-   like a zero norm or zero cross product and returning a correct result
-   or raising an appropriate error.
-
-
 Development
 -----------
 
+First, [install Poetry][].
+
 After cloning the repo, run `./bootstrap.zsh` to initialize a virtual
 environment with the project's dependencies.
 
 Subsequently, run `./dev.py install` to update the dependencies.
 
-
-Versioning
-----------
-
-This library adheres to [Semantic Versioning][semver].
-
-[semver]: https://semver.org/
+[install poetry]: https://python-poetry.org/docs/#installation
 
 
 Acknowledgements
diff --git a/doc/index.md b/doc/index.md
index 94503b2..84d8292 100644
--- a/doc/index.md
+++ b/doc/index.md
@@ -112,13 +112,57 @@ These common operations should be abstracted for a few reasons:
    or raising an appropriate error.
 
 
-Versioning
-----------
+Future-proofing your application or library
+-------------------------------------------
 
 This library adheres to [Semantic Versioning][semver].
 
 [semver]: https://semver.org/
 
+Since Python can accommodate only one installation of a package, using a
+toolbelt like `vg` as a transitive dependency can be a particular challenge, as
+various dependencies in the tree may rely on different versions of vg.
+
+One option would be to avoid making breaking changes forevever. However this is 
+antithetical to one of the goals of the project, which is to make a friendly
+interface for doing linear algebra. Experience has shown that over the years,
+we get clearer about what does and doesn't belong in this library, and what ways
+of exposing this functionality are easiest to learn. We want to continue to
+improve the interface over time, even if it means small breaking changes.
+
+As a result, we provide a forward compatibility layer, which all libraries
+depending on `vg` are encouraged to use. Replace `import vg` with
+`from vg.compat import v1 as vg` and use `>=1.11` as your dependency specifier.
+You can also replace 1.11 with a later version which includes a feature you
+need. The important thing is not to use `>=1.11,<2`. Since this project
+guarantees that `from vg.compat import v1 as vg` will continue to work the same
+in 2.0+, the `<2` constraint provides no stability value &ndash; and it makes
+things unnecessarily difficult for consumers who use multiple dependencies with
+`vg`.
+
+Applications have two options:
+
+1. Follow the recommendation for libraries: specify `>=1.11` and import using
+   `from vg.compat import v1 as vg`. This option provides better code stability
+   and makes upgrades seamless.
+2. Specify `>=1.11,<2` and use `import vg` directly, and when upgrading to
+   `>=2,<3`, review the changelog and modify the calling code if necessary.
+   This option ensures you stay up to date with the recommended, friendliest
+   interface for calling into `vg`.
+
+### Breaking changes
+
+The project's goal is to limit breaking changes to the API to every one to two
+years. This means breaking changes must be batched. Typically such features are
+first made available under the `vg.experimental` module, and then moved into
+`vg` upon the next major version release. Such experimental features may change
+in any subsequent minor release.
+
+### Deprecations
+
+Deprecated features will emit deprecation warnings in a minor version and cause
+errors or incorrect behavior in the next major version.
+
 
 If you like vg you might also like &hellip;
 -------------------------------------------
diff --git a/vg/compat/v1.py b/vg/compat/v1.py
new file mode 100644
index 0000000..31d9d3e
--- /dev/null
+++ b/vg/compat/v1.py
@@ -0,0 +1,1 @@
+from ..core import *  # noqa: F401, F403


Test patch:
diff --git a/vg/compat/__init__.py b/vg/compat/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/vg/compat/test_v1.py b/vg/compat/test_v1.py
new file mode 100644
index 0000000..d22eb4e
--- /dev/null
+++ b/vg/compat/test_v1.py
@@ -0,0 +1,21 @@
+import numpy as np
+import pytest
+import vg.compat.v1 as vg
+
+
+def test_v1_has_functions():
+    np.testing.assert_array_equal(
+        vg.normalize(np.array([5, 0, 0])), np.array([1, 0, 0])
+    )
+
+
+def test_v1_has_constants():
+    np.testing.assert_array_equal(vg.basis.x, np.array([1, 0, 0]))
+
+
+def test_v1_orient_is_alias_for_aligned_with():
+    v1 = np.array([1.0, 2.0, 3.0])
+    with pytest.deprecated_call():
+        np.testing.assert_array_equal(
+            vg.orient(v1, along=vg.basis.z), vg.aligned_with(v1, along=vg.basis.z)
+        )