diff --git a/.circleci/config.yml b/.circleci/config.yml
index 9ef615cc7..f7e564a85 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -5,7 +5,7 @@ jobs:
     working_directory: ~/openapi-diff
 
     docker:
-      - image: circleci/openjdk:8-jdk-node-browsers
+      - image: cimg/openjdk:8.0.442
 
     steps:
 
diff --git a/.dockerignore b/.dockerignore
new file mode 100644
index 000000000..92322c4e0
--- /dev/null
+++ b/.dockerignore
@@ -0,0 +1,2 @@
+.idea/
+target/
diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml
index 6d94e58c1..8c337389e 100644
--- a/.github/workflows/maven.yml
+++ b/.github/workflows/maven.yml
@@ -20,15 +20,15 @@ jobs:
     strategy:
       fail-fast: false
       matrix:
-        java_version: ['8', '11', '17']
+        java_version: ['8', '11', '17', '21']
     env:
       JAVA_OPTS: "-XX:+TieredCompilation -XX:TieredStopAtLevel=1"
     steps:
-    - uses: actions/checkout@v2.4.0
+    - uses: actions/checkout@v4
       with:
         fetch-depth: 0  # Shallow clones should be disabled for a better relevancy of analysis
     - name: Set up JDK
-      uses: actions/setup-java@v2
+      uses: actions/setup-java@v4
       with:
         distribution: 'zulu'
         java-version: ${{ matrix.java_version }}
@@ -38,8 +38,8 @@ jobs:
         server-username: OSS_USERNAME
         server-password: OSS_PASSWORD
     - name: Cache SonarCloud packages
-      uses: actions/cache@v2.1.7
-      if: ${{ env.SONAR_TOKEN != null && env.SONAR_TOKEN != '' && matrix.java_version == '11' }}
+      uses: actions/cache@v4
+      if: ${{ env.SONAR_TOKEN != null && env.SONAR_TOKEN != '' && matrix.java_version == '17' }}
       env:
         SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
       with:
@@ -49,7 +49,7 @@ jobs:
     - name: Run tests
       run: ./mvnw -V -B -ntp -ff verify jacoco:report
     - name: Static Analysis (Sonar)
-      if: ${{ env.SONAR_TOKEN != null && env.SONAR_TOKEN != '' && matrix.java_version == '11' }}
+      if: ${{ env.SONAR_TOKEN != null && env.SONAR_TOKEN != '' && matrix.java_version == '17' }}
       env:
         GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
         SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml
index 9b68efe15..d3a0a7bb7 100644
--- a/.github/workflows/pr.yml
+++ b/.github/workflows/pr.yml
@@ -17,20 +17,20 @@ jobs:
     strategy:
       fail-fast: false
       matrix:
-        java_version: ['8', '11', '17']
+        java_version: ['8', '11', '17', '21']
     env:
       JAVA_OPTS: "-XX:+TieredCompilation -XX:TieredStopAtLevel=1"
     steps:
-      - uses: actions/checkout@v2.4.0
+      - uses: actions/checkout@v4
       - name: Set up JDK
-        uses: actions/setup-java@v2
+        uses: actions/setup-java@v4
         with:
           distribution: 'zulu'
           java-version: ${{ matrix.java_version }}
           cache: 'maven'
       - name: Cache SonarCloud packages
-        uses: actions/cache@v2.1.7
-        if: ${{ env.SONAR_TOKEN != null && env.SONAR_TOKEN != '' && matrix.java_version == '11' }}
+        uses: actions/cache@v4
+        if: ${{ env.SONAR_TOKEN != null && env.SONAR_TOKEN != '' && matrix.java_version == '17' }}
         env:
           SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
         with:
@@ -40,7 +40,7 @@ jobs:
       - name: Run tests
         run: ./mvnw -V -B -ntp -ff verify
       - name: Static Analysis (Sonar)
-        if: ${{ env.SONAR_TOKEN != null && env.SONAR_TOKEN != '' && matrix.java_version == '11' }}
+        if: ${{ env.SONAR_TOKEN != null && env.SONAR_TOKEN != '' && matrix.java_version == '17' }}
         env:
           GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
           SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 6d5f5c4c6..94d5c8ba5 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -16,9 +16,9 @@ jobs:
   release:
     runs-on: ubuntu-latest
     steps:
-      - uses: actions/checkout@v2.4.0
+      - uses: actions/checkout@v4
       - name: Set up JDK 11
-        uses: actions/setup-java@v2
+        uses: actions/setup-java@v4
         with:
           distribution: 'zulu'
           java-version: 11
@@ -46,21 +46,24 @@ jobs:
     needs: release
     runs-on: ubuntu-latest
     steps:
-      - uses: actions/checkout@v2.4.0
+      - uses: actions/checkout@v4
         with:
           ref: ${{ github.event.inputs.releaseVersion }}
+      - name: Set up QEMU
+        uses: docker/setup-qemu-action@v3
       - name: Set up Docker Buildx
-        uses: docker/setup-buildx-action@v1
+        uses: docker/setup-buildx-action@v3
       - name: Login to DockerHub
-        uses: docker/login-action@v1 
+        uses: docker/login-action@v3 
         with:
           username: ${{ secrets.DOCKER_HUB_USERNAME }}
           password: ${{ secrets.DOCKER_HUB_PASSWORD }}
       - name: Build & Push Docker image
-        uses: docker/build-push-action@v2
+        uses: docker/build-push-action@v6
         with:
           context: .
           push: true
+          platforms: linux/amd64,linux/arm64
           tags: |
             openapitools/openapi-diff:${{ github.event.inputs.releaseVersion }}
             openapitools/openapi-diff:latest
diff --git a/.gitignore b/.gitignore
index 7956bcbd3..7da72d7f2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,8 +2,6 @@
 
 .classpath
 .project
-.settings/*
-.idea
 *.iml
 
 # Mobile Tools for Java (J2ME)
@@ -17,3 +15,8 @@
 # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
 hs_err_pid*
 target/
+
+# IDE files
+.settings
+.idea
+.vscode
diff --git a/.gitpod.yml b/.gitpod.yml
new file mode 100644
index 000000000..1dd7933ef
--- /dev/null
+++ b/.gitpod.yml
@@ -0,0 +1,137 @@
+## Learn more about this file at 'https://www.gitpod.io/docs/references/gitpod-yml'
+##
+## This '.gitpod.yml' file when placed at the root of a project instructs
+## Gitpod how to prepare & build the project, start development environments
+## and configure continuous prebuilds. Prebuilds when enabled builds a project
+## like a CI server so you can start coding right away - no more waiting for
+## dependencies to download and builds to finish when reviewing pull-requests
+## or hacking on something new.
+##
+## With Gitpod you can develop software from any device (even iPads) via 
+## desktop or browser based versions of VS Code or any JetBrains IDE and
+## customise it to your individual needs - from themes to extensions, you
+## have full control.
+##
+## The easiest way to try out Gitpod is install the browser extenion:
+## 'https://www.gitpod.io/docs/browser-extension' or by prefixing
+## 'https://gitpod.io#' to the source control URL of any project.
+##
+## For example: 'https://gitpod.io#https://github.com/gitpod-io/gitpod'
+
+
+## The 'image' section defines which Docker image Gitpod should use. 
+## By default, Gitpod uses a standard Docker Image called 'workspace-full'
+## which can be found at 'https://github.com/gitpod-io/workspace-images'
+##
+## Workspaces started based on this default image come pre-installed with
+## Docker, Go, Java, Node.js, C/C++, Python, Ruby, Rust, PHP as well as
+## tools such as Homebrew, Tailscale, Nginx and several more.
+##
+## If this image does not include the tools needed for your project then
+## a public Docker image or your own Docker file can be configured.
+## 
+## Learn more about images at 'https://www.gitpod.io/docs/config-docker'
+
+#image: node:buster                        # use 'https://hub.docker.com/_/node'
+#
+#image:                                    # leave image undefined if using a Dockerfile
+#  file: .gitpod.Dockerfile                # relative path to the Dockerfile from the
+#                                          # root of the project
+
+## The 'tasks' section defines how Gitpod prepares and builds this project
+## or how Gitpod can start development servers. With Gitpod, there are three
+## types of tasks:
+##
+## - before: Use this for tasks that need to run before init and before command. 
+## - init: Use this to configure prebuilds of heavy-lifting tasks such as
+##         downloading dependencies or compiling source code.
+## - command: Use this to start your database or application when the workspace starts.
+##
+## Learn more about these tasks at 'https://www.gitpod.io/docs/config-start-tasks'
+
+#tasks:
+#  - before: |
+#      # commands to execute...
+#
+#  - init: |
+#      # sudo apt-get install python3     # can be used to install operating system 
+#                                         # dependencies but these are not kept after the
+#                                         # prebuild completes thus Gitpod recommends moving
+#                                         # operating system dependency installation steps
+#                                         # to a custom Dockerfile to make prebuilds faster
+#                                         # and to keep your codebase DRY.  
+#                                         # 'https://www.gitpod.io/docs/config-docker'
+#
+#      # pip install -r requirements.txt  # install codebase dependencies
+#      # cmake                            # precompile codebase
+#
+#  - name: Web Server
+#    openMode: split-left
+#    env:
+#      WEBSERVER_PORT: 8080
+#    command: |
+#     python3 -m http.server $WEBSERVER_PORT
+#
+#  - name: Web Browser
+#    openMode: split-right
+#    env:
+#      WEBSERVER_PORT: 8080
+#    command: |
+#     gp await-port $WEBSERVER_PORT
+#     lynx `gp url`
+
+tasks:
+  - init: ./mvnw package -DskipTests
+
+## The 'ports' section defines various ports your may listen on are 
+## configured in Gitpod on an authenticated URL. By default, all ports
+## are in private visibility state.
+##
+## Learn more about ports at 'https://www.gitpod.io/docs/config-ports'
+
+#ports:
+#  - port: 8080 # alternatively configure entire ranges via '8080-8090'
+#    visibility: private # either 'public' or 'private' (default)
+#    onOpen: open-browser # either 'open-browser', 'open-preview' or 'ignore'
+
+
+## The 'vscode' section defines a list of Visual Studio Code extensions from
+## the OpenVSX.org registry to be installed upon workspace startup. OpenVSX
+## is an open alternative to the proprietary Visual Studio Code Marketplace
+## and extensions can be added by sending a pull-request with the extension
+## identifier to https://github.com/open-vsx/publish-extensions
+##
+## The identifier of an extension is always ${publisher}.${name}.
+##
+## For example: 'vscodevim.vim'
+##
+## Learn more at 'https://www.gitpod.io/docs/ides-and-editors/vscode'
+
+vscode:
+  extensions:
+    - redhat.java
+    - vscjava.vscode-java-pack
+
+## The 'github' section defines configuration of continuous prebuilds
+## for GitHub repositories when the GitHub application
+## 'https://github.com/apps/gitpod-io' is installed in GitHub and granted
+## permissions to access the repository.
+##
+## Learn more at 'https://www.gitpod.io/docs/prebuilds'
+
+github: 
+  prebuilds:
+    # enable for the default branch
+    master: true
+    # enable for all branches in this repo
+    branches: true
+    # enable for pull requests coming from this repo
+    pullRequests: true
+    # enable for pull requests coming from forks
+    pullRequestsFromForks: true
+    # add a check to pull requests
+    addCheck: true
+    # add a "Review in Gitpod" button as a comment to pull requests
+    addComment: true
+    # add a "Review in Gitpod" button to the pull request's description
+    addBadge: false
diff --git a/.mvn/wrapper/maven-wrapper.jar b/.mvn/wrapper/maven-wrapper.jar
index c6feb8bb6..bf82ff01c 100644
Binary files a/.mvn/wrapper/maven-wrapper.jar and b/.mvn/wrapper/maven-wrapper.jar differ
diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties
index 31158c7a0..687668b39 100644
--- a/.mvn/wrapper/maven-wrapper.properties
+++ b/.mvn/wrapper/maven-wrapper.properties
@@ -1 +1,18 @@
-distributionUrl=https://repo1.maven.org/maven2/org/apache/maven/apache-maven/3.8.4/apache-maven-3.8.4-bin.zip
\ No newline at end of file
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.5/apache-maven-3.8.5-bin.zip
+wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.1/maven-wrapper-3.1.1.jar
diff --git a/Dockerfile b/Dockerfile
index 72bd50130..b07fd3dda 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,10 +1,16 @@
-FROM openjdk:8-jdk-slim AS build
+FROM eclipse-temurin:21-jdk-alpine AS build
 WORKDIR /build
 COPY ./ /build
 RUN ./mvnw -V -B -ff -P docker package -q
 
-FROM openjdk:8-jre-slim
+FROM eclipse-temurin:21-jre-alpine
 WORKDIR /app
-COPY --from=build /build/cli/target/openapi-diff-cli-*-all.jar /app/openapi-diff.jar
-ENTRYPOINT ["java", "-jar", "/app/openapi-diff.jar"]
+COPY --from=build /build/cli/target/openapi-diff.jar /app/openapi-diff.jar
+COPY --from=build /build/core/src/test/resources/petstore_v2_* /tmp
+RUN java -XX:ArchiveClassesAtExit=/app/appcds.jsa \
+    -jar /app/openapi-diff.jar \
+    /tmp/petstore_v2_1.yaml \
+    /tmp/petstore_v2_2.yaml \
+    && rm -f /tmp/petstore_v2_*
+ENTRYPOINT ["java", "-Xshare:on", "-XX:SharedArchiveFile=/app/appcds.jsa", "-jar", "/app/openapi-diff.jar"]
 CMD ["--help"]
diff --git a/README.md b/README.md
index 95648473e..e4979c1e3 100644
--- a/README.md
+++ b/README.md
@@ -1,14 +1,17 @@
 # OpenAPI-diff 
 
-Compare two OpenAPI specifications (3.x) and render the difference to HTML plaintext, or Markdown files.
+Compare two OpenAPI specifications (3.x) and render the difference to HTML plaintext, Markdown files, or JSON files.
 
 [![Build](https://github.com/OpenAPITools/openapi-diff/workflows/Main%20Build/badge.svg)](https://github.com/OpenAPITools/openapi-diff/actions?query=branch%3Amaster+workflow%3A"Main+Build")
 [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=OpenAPITools_openapi-diff&metric=alert_status)](https://sonarcloud.io/dashboard?id=OpenAPITools_openapi-diff)
-[![Maven Central](https://img.shields.io/maven-central/v/org.openapitools.openapidiff/openapi-diff-core)](https://search.maven.org/artifact/org.openapitools.openapidiff/openapi-diff-core)
-[![Join the Slack chat room](https://img.shields.io/badge/Slack-Join%20the%20chat%20room-orange)](https://join.slack.com/t/openapi-generator/shared_invite/enQtNzAyNDMyOTU0OTE1LTY5ZDBiNDI5NzI5ZjQ1Y2E5OWVjMjZkYzY1ZGM2MWQ4YWFjMzcyNDY5MGI4NjQxNDBiMTlmZTc5NjY2ZTQ5MGM)
 
-[![Docker Cloud Automated build](https://img.shields.io/docker/cloud/automated/openapitools/openapi-diff)](https://hub.docker.com/r/openapitools/openapi-diff)
-[![Docker Cloud Build Status](https://img.shields.io/docker/cloud/build/openapitools/openapi-diff)](https://hub.docker.com/r/openapitools/openapi-diff)
+[![Maven Central 2.0.x](https://img.shields.io/maven-central/v/org.openapitools.openapidiff/openapi-diff-core?versionPrefix=2.0)](https://search.maven.org/artifact/org.openapitools.openapidiff/openapi-diff-core)
+[![Maven Central 2.1.x](https://img.shields.io/maven-central/v/org.openapitools.openapidiff/openapi-diff-core?versionPrefix=2.1)](https://search.maven.org/artifact/org.openapitools.openapidiff/openapi-diff-core)
+
+[![Contribute with Gitpod](https://img.shields.io/badge/Contribute%20with-Gitpod-908a85?logo=gitpod)](https://gitpod.io/#https://github.com/OpenAPITools/openapi-diff)
+[![Join the Slack chat room](https://img.shields.io/badge/Slack-Join%20the%20chat%20room-orange)](https://join.slack.com/t/openapi-generator/shared_invite/zt-12jxxd7p2-XUeQM~4pzsU9x~eGLQqX2g)
+
+[![Docker Automated build](https://img.shields.io/docker/automated/openapitools/openapi-diff)](https://hub.docker.com/r/openapitools/openapi-diff)
 [![Docker Image Version](https://img.shields.io/docker/v/openapitools/openapi-diff?sort=semver)](https://hub.docker.com/r/openapitools/openapi-diff/tags)
 
 # Requirements
@@ -21,7 +24,7 @@ Compare two OpenAPI specifications (3.x) and render the difference to HTML plain
 * Depth comparison of parameters, responses, endpoint, http method (GET,POST,PUT,DELETE...)
 * Supports swagger api Authorization
 * Render difference of property with Expression Language
-* HTML, Markdown & JSON render
+* HTML, Markdown, Asciidoc & JSON render
 
 # Maven
 
@@ -35,6 +38,14 @@ Available on [Maven Central](https://search.maven.org/artifact/org.openapitools.
 </dependency>
 ```
 
+# Homebrew
+Available for Mac users on [brew](https://formulae.brew.sh/formula/openapi-diff)
+
+```bash
+brew install openapi-diff
+```
+Usage instructions in [Usage -> Command line](#command-line)
+
 # Docker
 
 Available on [Docker Hub](https://hub.docker.com/r/openapitools/openapi-diff/) as `openapitools/openapi-diff`.
@@ -42,12 +53,15 @@ Available on [Docker Hub](https://hub.docker.com/r/openapitools/openapi-diff/) a
 ```bash
 # docker run openapitools/openapi-diff:latest
 usage: openapi-diff <old> <new>
+    --asciidoc <file>           export diff as asciidoc in given file
     --debug                     Print debugging information
     --error                     Print error information
     --fail-on-changed           Fail if API changed but is backward
                                 compatible
     --fail-on-incompatible      Fail only if API changes broke backward
                                 compatibility
+    --config-file               Config file to override default behavior. Supported file formats: .yaml
+    --config-prop               Config property to override default behavior with key:value format (e.g. my.prop:true)
  -h,--help                      print this message
     --header <property=value>   use given header for authorisation
     --html <file>               export diff as html in given file
@@ -66,7 +80,6 @@ usage: openapi-diff <old> <new>
     --warn                      Print warning information
 ```
 
-
 ## Build the image
 
 This is only required if you want to try new changes in the Dockerfile of this project.
@@ -99,6 +112,7 @@ openapi-diff can read OpenAPI specs from JSON files or HTTP URLs.
 ```bash
 $ openapi-diff --help
 usage: openapi-diff <old> <new>
+    --asciidoc <file>           export diff as asciidoc in given file
     --debug                     Print debugging information
     --error                     Print error information
  -h,--help                      print this message
@@ -115,6 +129,8 @@ usage: openapi-diff <old> <new>
                                 incompatible, compatible
     --fail-on-incompatible      Fail only if API changes broke backward compatibility
     --fail-on-changed           Fail if API changed but is backward compatible
+    --config-file               Config file to override default behavior. Supported file formats: .yaml
+    --config-prop               Config property to override default behavior with key:value format (e.g. my.prop:true)
     --trace                     be extra verbose
     --version                   print the version information and exit
     --warn                      Print warning information
@@ -142,7 +158,21 @@ Add openapi-diff to your POM to show diffs when you test your Maven project. You
         <!-- Fail only if API changes broke backward compatibility (default: false) -->
         <failOnIncompatible>true</failOnIncompatible>
         <!-- Fail if API changed (default: false) -->
-        <failOnChanged>true</failOnIncompatible>
+        <failOnChanged>true</failOnChanged>
+        <!-- Supply file path for console output to file if desired. -->
+        <consoleOutputFileName>${project.basedir}/../maven/target/diff.txt</consoleOutputFileName>
+        <!-- Supply file path for json output to file if desired. -->
+        <jsonOutputFileName>${project.basedir}/../maven/target/diff.json</jsonOutputFileName>
+        <!-- Supply file path for markdown output to file if desired. -->
+        <markdownOutputFileName>${project.basedir}/../maven/target/diff.md</markdownOutputFileName>
+        <!-- Supply config file(s), e.g. to disable incompatibility checks. Later files override earlier files -->
+        <configFiles>
+          <configFile>my/config-file.yaml</configFile>
+        </configFiles>
+        <!-- Supply config properties, e.g. to disable incompatibility checks. Overrides configFiles. -->
+        <configProps>
+          <incompatible.response.enum.increased>false</incompatible.response.enum.increased>
+        </configProps>
       </configuration>
     </execution>
   </executions>
@@ -165,54 +195,42 @@ public class Main {
 ```
 
 ### Render difference
+
 ---
 #### HTML
 
 ```java
-String html = new HtmlRender("Changelog",
-        "http://deepoove.com/swagger-diff/stylesheets/demo.css")
-                .render(diff);
-
-try {
-    FileWriter fw = new FileWriter(
-            "testNewApi.html");
-    fw.write(html);
-    fw.close();
-
-} catch (IOException e) {
-    e.printStackTrace();
-}
+HtmlRender htmlRender = new HtmlRender("Changelog", "http://deepoove.com/swagger-diff/stylesheets/demo.css");
+FileOutputStream outputStream = new FileOutputStream("testDiff.html");
+OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream);
+htmlRender.render(diff, outputStreamWriter);
 ```
 
 #### Markdown
 
 ```java
-String render = new MarkdownRender().render(diff);
-try {
-    FileWriter fw = new FileWriter(
-            "testDiff.md");
-    fw.write(render);
-    fw.close();
-    
-} catch (IOException e) {
-    e.printStackTrace();
-}
+MarkdownRender markdownRender = new MarkdownRender();
+FileOutputStream outputStream = new FileOutputStream("testDiff.md");
+OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream);
+markdownRender.render(diff, outputStreamWriter);
 ```
 
+#### Asciidoc
+
+```java
+AsciidocRender asciidocRender = new AsciidocRender();
+FileOutputStream outputStream = new FileOutputStream("testDiff.adoc");
+OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream);
+asciidocRender.render(diff, outputStreamWriter);
+```
 
 #### JSON
 
 ```java
-String render = new JsonRender().render(diff);
-try {
-    FileWriter fw = new FileWriter(
-            "testDiff.json");
-    fw.write(render);
-    fw.close();
-    
-} catch (IOException e) {
-    e.printStackTrace();
-}
+JsonRender jsonRender = new JsonRender();
+FileOutputStream outputStream = new FileOutputStream("testDiff.json");
+OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream);
+jsonRender.render(diff, outputStreamWriter);
 ```
 
 ### Extensions
diff --git a/cli/pom.xml b/cli/pom.xml
index 66c18fb22..573eb6c42 100644
--- a/cli/pom.xml
+++ b/cli/pom.xml
@@ -4,7 +4,7 @@
     <parent>
         <groupId>org.openapitools.openapidiff</groupId>
         <artifactId>openapi-diff-parent</artifactId>
-        <version>2.0.1-SNAPSHOT</version>
+        <version>2.1.0-SNAPSHOT</version>
     </parent>
 
     <artifactId>openapi-diff-cli</artifactId>
@@ -13,6 +13,10 @@
     <name>openapi-diff-cli</name>
     <description>CLI for openapi-diff</description>
 
+    <properties>
+        <mainClass>org.openapitools.openapidiff.cli.Main</mainClass>
+    </properties>
+
     <dependencies>
         <dependency>
             <groupId>org.openapitools.openapidiff</groupId>
@@ -31,8 +35,8 @@
             <artifactId>slf4j-api</artifactId>
         </dependency>
         <dependency>
-            <groupId>org.slf4j</groupId>
-            <artifactId>slf4j-log4j12</artifactId>
+            <groupId>ch.qos.logback</groupId>
+            <artifactId>logback-classic</artifactId>
         </dependency>
     </dependencies>
     <build>
@@ -52,7 +56,7 @@
                             <shadedClassifierName>all</shadedClassifierName>
                             <transformers>
                                 <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
-                                    <mainClass>org.openapitools.openapidiff.cli.Main</mainClass>
+                                    <mainClass>${mainClass}</mainClass>
                                 </transformer>
                               <transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer" />
                               <transformer implementation="org.apache.maven.plugins.shade.resource.ApacheLicenseResourceTransformer" />
@@ -64,4 +68,41 @@
             </plugin>
         </plugins>
     </build>
+
+    <profiles>
+        <profile>
+            <id>docker</id>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-shade-plugin</artifactId>
+                        <executions>
+                            <execution>
+                                <phase>package</phase>
+                                <goals>
+                                    <goal>shade</goal>
+                                </goals>
+                                <configuration>
+                                    <createDependencyReducedPom>false</createDependencyReducedPom>
+                                    <finalName>openapi-diff</finalName>
+                                    <transformers>
+                                        <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
+                                            <mainClass>${mainClass}</mainClass>
+                                            <manifestEntries>
+                                                <Implementation-Version>${project.version}</Implementation-Version>
+                                            </manifestEntries>
+                                        </transformer>
+                                        <transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer" />
+                                        <transformer implementation="org.apache.maven.plugins.shade.resource.ApacheLicenseResourceTransformer" />
+                                        <transformer implementation="org.apache.maven.plugins.shade.resource.ApacheNoticeResourceTransformer" />
+                                    </transformers>
+                                </configuration>
+                            </execution>
+                        </executions>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+    </profiles>
 </project>
diff --git a/cli/src/main/java/org/openapitools/openapidiff/cli/Main.java b/cli/src/main/java/org/openapitools/openapidiff/cli/Main.java
index 8da0fea16..a9e223d27 100644
--- a/cli/src/main/java/org/openapitools/openapidiff/cli/Main.java
+++ b/cli/src/main/java/org/openapitools/openapidiff/cli/Main.java
@@ -1,9 +1,11 @@
 package org.openapitools.openapidiff.cli;
 
+import ch.qos.logback.classic.Level;
 import io.swagger.v3.parser.core.models.AuthorizationValue;
+import java.io.ByteArrayOutputStream;
 import java.io.File;
-import java.io.IOException;
-import java.nio.charset.StandardCharsets;
+import java.io.FileOutputStream;
+import java.io.OutputStreamWriter;
 import java.util.Collections;
 import java.util.List;
 import org.apache.commons.cli.CommandLine;
@@ -13,12 +15,11 @@
 import org.apache.commons.cli.Option;
 import org.apache.commons.cli.Options;
 import org.apache.commons.cli.ParseException;
-import org.apache.commons.io.FileUtils;
 import org.apache.commons.lang3.exception.ExceptionUtils;
-import org.apache.log4j.Level;
-import org.apache.log4j.LogManager;
 import org.openapitools.openapidiff.core.OpenApiCompare;
+import org.openapitools.openapidiff.core.compare.OpenApiDiffOptions;
 import org.openapitools.openapidiff.core.model.ChangedOpenApi;
+import org.openapitools.openapidiff.core.output.AsciidocRender;
 import org.openapitools.openapidiff.core.output.ConsoleRender;
 import org.openapitools.openapidiff.core.output.HtmlRender;
 import org.openapitools.openapidiff.core.output.JsonRender;
@@ -34,7 +35,10 @@ public static void main(String... args) {
     Options options = new Options();
     options.addOption(Option.builder("h").longOpt("help").desc("print this message").build());
     options.addOption(
-        Option.builder().longOpt("version").desc("print the version information and exit").build());
+        Option.builder("v")
+            .longOpt("version")
+            .desc("print the version information and exit")
+            .build());
     options.addOption(
         Option.builder()
             .longOpt("state")
@@ -50,6 +54,19 @@ public static void main(String... args) {
             .longOpt("fail-on-changed")
             .desc("Fail if API changed but is backward compatible")
             .build());
+    options.addOption(
+        Option.builder()
+            .longOpt("config-file")
+            .hasArg()
+            .desc("Config file to override default behavior. Supported file formats: .yaml")
+            .build());
+    options.addOption(
+        Option.builder()
+            .longOpt("config-prop")
+            .hasArg()
+            .desc(
+                "Config property to override default behavior. Arg in format of [propKey]:[propVal]")
+            .build());
     options.addOption(Option.builder().longOpt("trace").desc("be extra verbose").build());
     options.addOption(
         Option.builder().longOpt("debug").desc("Print debugging information").build());
@@ -58,6 +75,7 @@ public static void main(String... args) {
     options.addOption(Option.builder().longOpt("warn").desc("Print warning information").build());
     options.addOption(Option.builder().longOpt("error").desc("Print error information").build());
     options.addOption(Option.builder().longOpt("off").desc("No information printed").build());
+    options.addOption(Option.builder().longOpt("off").desc("No information printed").build());
     options.addOption(
         Option.builder("l")
             .longOpt("log")
@@ -90,12 +108,26 @@ public static void main(String... args) {
             .argName("file")
             .desc("export diff as markdown in given file")
             .build());
+    options.addOption(
+        Option.builder()
+            .longOpt("asciidoc")
+            .hasArg()
+            .argName("file")
+            .desc("export diff as asciidoc in given file")
+            .build());
     options.addOption(
         Option.builder()
             .longOpt("html")
             .hasArg()
             .argName("file")
-            .desc("export diff as html in given file")
+            .desc("export diff as html in given file with incompatible changes")
+            .build());
+    options.addOption(
+        Option.builder()
+            .longOpt("html-detailed")
+            .hasArg()
+            .argName("file")
+            .desc("export diff as html in given file with all changes")
             .build());
     options.addOption(
         Option.builder()
@@ -121,6 +153,11 @@ public static void main(String... args) {
         printHelp(options);
         System.exit(0);
       }
+      if (line.hasOption("version") || line.hasOption("v")) {
+        String version = Main.class.getPackage().getImplementationVersion();
+        System.out.println("openapi-diff version: " + (version != null ? version : "DEV"));
+        System.exit(0);
+      }
       String logLevel = "ERROR";
       if (line.hasOption("off")) {
         logLevel = "OFF";
@@ -157,7 +194,9 @@ public static void main(String... args) {
       if (line.hasOption("state")) {
         logLevel = "OFF";
       }
-      LogManager.getRootLogger().setLevel(Level.toLevel(logLevel));
+      ch.qos.logback.classic.Logger root =
+          (ch.qos.logback.classic.Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME);
+      root.setLevel(Level.toLevel(logLevel));
 
       if (line.getArgList().size() < 2) {
         throw new ParseException("Missing arguments");
@@ -171,33 +210,68 @@ public static void main(String... args) {
         auths = Collections.singletonList(new AuthorizationValue(headers[0], headers[1], "header"));
       }
 
-      ChangedOpenApi result = OpenApiCompare.fromLocations(oldPath, newPath, auths);
+      OpenApiDiffOptions.Builder optionBuilder = OpenApiDiffOptions.builder();
+      String[] configFilePaths = line.getOptionValues("config-file");
+      if (configFilePaths != null) {
+        for (String configFilePath : configFilePaths) {
+          optionBuilder.configYaml(new File(configFilePath));
+        }
+      }
+
+      String[] configProps = line.getOptionValues("config-prop");
+      if (configProps != null) {
+        for (String propKeyAndVal : configProps) {
+          String[] split = propKeyAndVal.split(":");
+          if (split.length != 2 || split[0].isEmpty() || split[1].isEmpty()) {
+            throw new IllegalArgumentException("--config-prop unexpected format: " + propKeyAndVal);
+          }
+          optionBuilder.configProperty(split[0], split[1]);
+        }
+      }
+      OpenApiDiffOptions compareOpts = optionBuilder.build();
+
+      ChangedOpenApi result = OpenApiCompare.fromLocations(oldPath, newPath, auths, compareOpts);
       ConsoleRender consoleRender = new ConsoleRender();
       if (!logLevel.equals("OFF")) {
-        System.out.println(consoleRender.render(result));
+        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+        OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream);
+        consoleRender.render(result, outputStreamWriter);
+        System.out.println(outputStream);
       }
       if (line.hasOption("html")) {
         HtmlRender htmlRender = new HtmlRender();
-        String output = htmlRender.render(result);
-        String outputFile = line.getOptionValue("html");
-        writeOutput(output, outputFile);
+        FileOutputStream outputStream = new FileOutputStream(line.getOptionValue("html"));
+        OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream);
+        htmlRender.render(result, outputStreamWriter);
+      }
+      if (line.hasOption("html-detailed")) {
+        HtmlRender htmlRender = new HtmlRender(true);
+        FileOutputStream outputStream = new FileOutputStream(line.getOptionValue("html-detailed"));
+        OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream);
+        htmlRender.render(result, outputStreamWriter);
       }
       if (line.hasOption("markdown")) {
         MarkdownRender mdRender = new MarkdownRender();
-        String output = mdRender.render(result);
-        String outputFile = line.getOptionValue("markdown");
-        writeOutput(output, outputFile);
+        FileOutputStream outputStream = new FileOutputStream(line.getOptionValue("markdown"));
+        OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream);
+        mdRender.render(result, outputStreamWriter);
+      }
+      if (line.hasOption("asciidoc")) {
+        AsciidocRender adocRender = new AsciidocRender();
+        FileOutputStream outputStream = new FileOutputStream(line.getOptionValue("asciidoc"));
+        OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream);
+        adocRender.render(result, outputStreamWriter);
       }
       if (line.hasOption("text")) {
-        String output = consoleRender.render(result);
-        String outputFile = line.getOptionValue("text");
-        writeOutput(output, outputFile);
+        FileOutputStream outputStream = new FileOutputStream(line.getOptionValue("text"));
+        OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream);
+        consoleRender.render(result, outputStreamWriter);
       }
       if (line.hasOption("json")) {
         JsonRender jsonRender = new JsonRender();
-        String output = jsonRender.render(result);
-        String outputFile = line.getOptionValue("json");
-        writeOutput(output, outputFile);
+        FileOutputStream outputStream = new FileOutputStream(line.getOptionValue("json"));
+        OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream);
+        jsonRender.render(result, outputStreamWriter);
       }
       if (line.hasOption("state")) {
         System.out.println(result.isChanged().getValue());
@@ -222,17 +296,6 @@ public static void main(String... args) {
     }
   }
 
-  private static void writeOutput(String output, String outputFile) {
-    File file = new File(outputFile);
-    logger.debug("Output file: {}", file.getAbsolutePath());
-    try {
-      FileUtils.writeStringToFile(file, output, StandardCharsets.UTF_8);
-    } catch (IOException e) {
-      logger.error("Impossible to write output to file {}", outputFile, e);
-      System.exit(2);
-    }
-  }
-
   public static void printHelp(Options options) {
     HelpFormatter formatter = new HelpFormatter();
     formatter.printHelp("openapi-diff <old> <new>", options);
diff --git a/cli/src/main/resources/log4j.properties b/cli/src/main/resources/log4j.properties
deleted file mode 100644
index 1f7d27e30..000000000
--- a/cli/src/main/resources/log4j.properties
+++ /dev/null
@@ -1,5 +0,0 @@
-log4j.rootLogger=DEBUG, STDOUT
-log4j.appender.STDOUT=org.apache.log4j.ConsoleAppender
-log4j.appender.STDOUT.layout=org.apache.log4j.PatternLayout
-log4j.appender.STDOUT.layout.ConversionPattern=%-5p [%c] - %m%n
-#log4j.appender.STDOUT.layout.ConversionPattern=%d{yyyy-MM-dd'T'HH:mm:ss.SSS} %-5p [%c] - %m%n
\ No newline at end of file
diff --git a/cli/src/main/resources/logback.xml b/cli/src/main/resources/logback.xml
new file mode 100644
index 000000000..018a4277c
--- /dev/null
+++ b/cli/src/main/resources/logback.xml
@@ -0,0 +1,11 @@
+<configuration>
+  <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
+    <encoder>
+      <pattern>[%thread] %-5level %logger{36} - %msg%n</pattern>
+    </encoder>
+  </appender>
+
+  <root level="debug">
+    <appender-ref ref="STDOUT"/>
+  </root>
+</configuration>
\ No newline at end of file
diff --git a/core/pom.xml b/core/pom.xml
index 261e92e8c..0f056b319 100644
--- a/core/pom.xml
+++ b/core/pom.xml
@@ -4,7 +4,7 @@
     <parent>
         <groupId>org.openapitools.openapidiff</groupId>
         <artifactId>openapi-diff-parent</artifactId>
-        <version>2.0.1-SNAPSHOT</version>
+        <version>2.1.0-SNAPSHOT</version>
     </parent>
 
     <artifactId>openapi-diff-core</artifactId>
@@ -25,6 +25,12 @@
         <dependency>
             <groupId>io.swagger.parser.v3</groupId>
             <artifactId>swagger-parser-v2-converter</artifactId>
+            <exclusions>
+                <exclusion>
+                    <groupId>commons-logging</groupId>
+                    <artifactId>commons-logging</artifactId>
+                </exclusion>
+            </exclusions>
         </dependency>
         <dependency>
             <groupId>com.j2html</groupId>
@@ -34,13 +40,17 @@
             <groupId>org.apache.commons</groupId>
             <artifactId>commons-collections4</artifactId>
         </dependency>
+        <dependency>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-configuration2</artifactId>
+        </dependency>
         <dependency>
             <groupId>org.slf4j</groupId>
             <artifactId>slf4j-api</artifactId>
         </dependency>
         <dependency>
-            <groupId>commons-httpclient</groupId>
-            <artifactId>commons-httpclient</artifactId>
+            <groupId>org.slf4j</groupId>
+            <artifactId>jcl-over-slf4j</artifactId>
         </dependency>
         <dependency>
             <groupId>org.junit.jupiter</groupId>
diff --git a/core/src/main/java/org/openapitools/openapidiff/core/OpenApiCompare.java b/core/src/main/java/org/openapitools/openapidiff/core/OpenApiCompare.java
index 4564733a5..daa3af9b1 100644
--- a/core/src/main/java/org/openapitools/openapidiff/core/OpenApiCompare.java
+++ b/core/src/main/java/org/openapitools/openapidiff/core/OpenApiCompare.java
@@ -7,6 +7,7 @@
 import java.io.File;
 import java.util.List;
 import org.openapitools.openapidiff.core.compare.OpenApiDiff;
+import org.openapitools.openapidiff.core.compare.OpenApiDiffOptions;
 import org.openapitools.openapidiff.core.model.ChangedOpenApi;
 
 public class OpenApiCompare {
@@ -40,7 +41,25 @@ public static ChangedOpenApi fromContents(String oldContent, String newContent)
    */
   public static ChangedOpenApi fromContents(
       String oldContent, String newContent, List<AuthorizationValue> auths) {
-    return fromSpecifications(readContent(oldContent, auths), readContent(newContent, auths));
+    return fromContents(oldContent, newContent, auths, OpenApiDiffOptions.builder().build());
+  }
+
+  /**
+   * compare two openapi doc
+   *
+   * @param oldContent old api-doc location:Json or Http
+   * @param newContent new api-doc location:Json or Http
+   * @param auths
+   * @param options
+   * @return Comparison result
+   */
+  public static ChangedOpenApi fromContents(
+      String oldContent,
+      String newContent,
+      List<AuthorizationValue> auths,
+      OpenApiDiffOptions options) {
+    return fromSpecifications(
+        readContent(oldContent, auths), readContent(newContent, auths), options);
   }
 
   /**
@@ -64,7 +83,21 @@ public static ChangedOpenApi fromFiles(File oldFile, File newFile) {
    */
   public static ChangedOpenApi fromFiles(
       File oldFile, File newFile, List<AuthorizationValue> auths) {
-    return fromLocations(oldFile.getAbsolutePath(), newFile.getAbsolutePath(), auths);
+    return fromFiles(oldFile, newFile, auths, OpenApiDiffOptions.builder().build());
+  }
+
+  /**
+   * compare two openapi doc
+   *
+   * @param oldFile old api-doc file
+   * @param newFile new api-doc file
+   * @param auths
+   * @param options
+   * @return Comparison result
+   */
+  public static ChangedOpenApi fromFiles(
+      File oldFile, File newFile, List<AuthorizationValue> auths, OpenApiDiffOptions options) {
+    return fromLocations(oldFile.getAbsolutePath(), newFile.getAbsolutePath(), auths, options);
   }
 
   /**
@@ -88,7 +121,25 @@ public static ChangedOpenApi fromLocations(String oldLocation, String newLocatio
    */
   public static ChangedOpenApi fromLocations(
       String oldLocation, String newLocation, List<AuthorizationValue> auths) {
-    return fromSpecifications(readLocation(oldLocation, auths), readLocation(newLocation, auths));
+    return fromLocations(oldLocation, newLocation, auths, OpenApiDiffOptions.builder().build());
+  }
+
+  /**
+   * compare two openapi doc
+   *
+   * @param oldLocation old api-doc location (local or http)
+   * @param newLocation new api-doc location (local or http)
+   * @param auths
+   * @param options
+   * @return Comparison result
+   */
+  public static ChangedOpenApi fromLocations(
+      String oldLocation,
+      String newLocation,
+      List<AuthorizationValue> auths,
+      OpenApiDiffOptions options) {
+    return fromSpecifications(
+        readLocation(oldLocation, auths), readLocation(newLocation, auths), options);
   }
 
   /**
@@ -99,7 +150,20 @@ public static ChangedOpenApi fromLocations(
    * @return Comparison result
    */
   public static ChangedOpenApi fromSpecifications(OpenAPI oldSpec, OpenAPI newSpec) {
-    return OpenApiDiff.compare(notNull(oldSpec, "old"), notNull(newSpec, "new"));
+    return fromSpecifications(oldSpec, newSpec, OpenApiDiffOptions.builder().build());
+  }
+
+  /**
+   * compare two openapi doc
+   *
+   * @param oldSpec old api-doc specification
+   * @param newSpec new api-doc specification
+   * @param options
+   * @return Comparison result
+   */
+  public static ChangedOpenApi fromSpecifications(
+      OpenAPI oldSpec, OpenAPI newSpec, OpenApiDiffOptions options) {
+    return OpenApiDiff.compare(notNull(oldSpec, "old"), notNull(newSpec, "new"), options);
   }
 
   private static OpenAPI notNull(OpenAPI spec, String type) {
diff --git a/core/src/main/java/org/openapitools/openapidiff/core/compare/ContentDiff.java b/core/src/main/java/org/openapitools/openapidiff/core/compare/ContentDiff.java
index b647172db..7cf27dca7 100644
--- a/core/src/main/java/org/openapitools/openapidiff/core/compare/ContentDiff.java
+++ b/core/src/main/java/org/openapitools/openapidiff/core/compare/ContentDiff.java
@@ -5,7 +5,9 @@
 
 import io.swagger.v3.oas.models.media.Content;
 import io.swagger.v3.oas.models.media.MediaType;
-import java.util.*;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
 import org.openapitools.openapidiff.core.model.Changed;
 import org.openapitools.openapidiff.core.model.ChangedContent;
 import org.openapitools.openapidiff.core.model.ChangedMediaType;
@@ -34,22 +36,15 @@ public DeferredChanged<ChangedContent> diff(Content left, Content right, DiffCon
               MediaType oldMediaType = left.get(mediaTypeKey);
               MediaType newMediaType = right.get(mediaTypeKey);
 
-              ChangedMediaType changedMediaType =
-                  new ChangedMediaType(oldMediaType.getSchema(), newMediaType.getSchema(), context);
-
               builder
                   .with(
                       openApiDiff
-                          .getSchemaDiff()
-                          .diff(
-                              oldMediaType.getSchema(),
-                              newMediaType.getSchema(),
-                              context.copyWithRequired(true)))
+                          .getMediaTypeDiff()
+                          .diff(oldMediaType, newMediaType, context.copyWithRequired(true)))
                   .ifPresent(
                       value -> {
-                        changedMediaType.setSchema(value);
-                        if (!isUnchanged(changedMediaType)) {
-                          changedMediaTypes.put(mediaTypeKey, changedMediaType);
+                        if (!isUnchanged(value)) {
+                          changedMediaTypes.put(mediaTypeKey, value);
                         }
                       });
             });
diff --git a/core/src/main/java/org/openapitools/openapidiff/core/compare/HeaderDiff.java b/core/src/main/java/org/openapitools/openapidiff/core/compare/HeaderDiff.java
index ecdfd5878..593c29df1 100644
--- a/core/src/main/java/org/openapitools/openapidiff/core/compare/HeaderDiff.java
+++ b/core/src/main/java/org/openapitools/openapidiff/core/compare/HeaderDiff.java
@@ -4,8 +4,9 @@
 import io.swagger.v3.oas.models.headers.Header;
 import java.util.HashSet;
 import java.util.Objects;
-import java.util.Optional;
 import org.openapitools.openapidiff.core.model.Changed;
+import org.openapitools.openapidiff.core.model.ChangedExample;
+import org.openapitools.openapidiff.core.model.ChangedExamples;
 import org.openapitools.openapidiff.core.model.ChangedHeader;
 import org.openapitools.openapidiff.core.model.DiffContext;
 import org.openapitools.openapidiff.core.model.deferred.DeferredBuilder;
@@ -44,12 +45,12 @@ protected DeferredChanged<ChangedHeader> computeDiff(
     DeferredBuilder<Changed> builder = new DeferredBuilder<>();
     ChangedHeader changedHeader =
         new ChangedHeader(left, right, context)
-            .setRequired(getBooleanDiff(left.getRequired(), right.getRequired()))
-            .setDeprecated(
-                !Boolean.TRUE.equals(left.getDeprecated())
-                    && Boolean.TRUE.equals(right.getDeprecated()))
+            .setRequired(!Objects.equals(left.getRequired(), right.getRequired()))
+            .setDeprecated(!Objects.equals(left.getDeprecated(), right.getDeprecated()))
             .setStyle(!Objects.equals(left.getStyle(), right.getStyle()))
-            .setExplode(getBooleanDiff(left.getExplode(), right.getExplode()));
+            .setExplode(!Objects.equals(left.getExplode(), right.getExplode()))
+            .setExamples(new ChangedExamples(left.getExamples(), right.getExamples()))
+            .setExample(new ChangedExample(left.getExample(), right.getExample()));
     builder
         .with(
             openApiDiff
@@ -73,10 +74,4 @@ protected DeferredChanged<ChangedHeader> computeDiff(
         .ifPresent(changedHeader::setExtensions);
     return builder.buildIsChanged(changedHeader);
   }
-
-  private boolean getBooleanDiff(Boolean left, Boolean right) {
-    boolean leftRequired = Optional.ofNullable(left).orElse(Boolean.FALSE);
-    boolean rightRequired = Optional.ofNullable(right).orElse(Boolean.FALSE);
-    return leftRequired != rightRequired;
-  }
 }
diff --git a/core/src/main/java/org/openapitools/openapidiff/core/compare/MediaTypeDiff.java b/core/src/main/java/org/openapitools/openapidiff/core/compare/MediaTypeDiff.java
new file mode 100644
index 000000000..a80324a92
--- /dev/null
+++ b/core/src/main/java/org/openapitools/openapidiff/core/compare/MediaTypeDiff.java
@@ -0,0 +1,40 @@
+package org.openapitools.openapidiff.core.compare;
+
+import static org.openapitools.openapidiff.core.utils.ChangedUtils.isChanged;
+
+import io.swagger.v3.oas.models.media.MediaType;
+import org.openapitools.openapidiff.core.model.Changed;
+import org.openapitools.openapidiff.core.model.ChangedExample;
+import org.openapitools.openapidiff.core.model.ChangedExamples;
+import org.openapitools.openapidiff.core.model.ChangedMediaType;
+import org.openapitools.openapidiff.core.model.DiffContext;
+import org.openapitools.openapidiff.core.model.deferred.DeferredBuilder;
+import org.openapitools.openapidiff.core.model.deferred.DeferredChanged;
+
+public class MediaTypeDiff {
+
+  private final OpenApiDiff openApiDiff;
+
+  public MediaTypeDiff(OpenApiDiff openApiDiff) {
+    this.openApiDiff = openApiDiff;
+  }
+
+  public DeferredChanged<ChangedMediaType> diff(
+      MediaType left, MediaType right, DiffContext context) {
+    DeferredBuilder<Changed> builder = new DeferredBuilder<>();
+
+    ChangedMediaType changedMediaType =
+        new ChangedMediaType(left.getSchema(), right.getSchema(), context)
+            .setExample(new ChangedExample(left.getExample(), right.getExample()))
+            .setExamples(new ChangedExamples(left.getExamples(), right.getExamples()));
+
+    builder
+        .with(
+            openApiDiff
+                .getSchemaDiff()
+                .diff(left.getSchema(), right.getSchema(), context.copyWithRequired(true)))
+        .ifPresent(changedMediaType::setSchema);
+
+    return builder.build().mapOptional(value -> isChanged(changedMediaType));
+  }
+}
diff --git a/core/src/main/java/org/openapitools/openapidiff/core/compare/OAuthFlowDiff.java b/core/src/main/java/org/openapitools/openapidiff/core/compare/OAuthFlowDiff.java
index 48e5e0447..8dd404f5b 100644
--- a/core/src/main/java/org/openapitools/openapidiff/core/compare/OAuthFlowDiff.java
+++ b/core/src/main/java/org/openapitools/openapidiff/core/compare/OAuthFlowDiff.java
@@ -8,6 +8,7 @@
 import java.util.Objects;
 import java.util.Optional;
 import org.openapitools.openapidiff.core.model.ChangedOAuthFlow;
+import org.openapitools.openapidiff.core.model.DiffContext;
 
 public class OAuthFlowDiff {
   private final OpenApiDiff openApiDiff;
@@ -20,8 +21,8 @@ private static Map<String, Object> getExtensions(OAuthFlow oAuthFlow) {
     return ofNullable(oAuthFlow).map(OAuthFlow::getExtensions).orElse(null);
   }
 
-  public Optional<ChangedOAuthFlow> diff(OAuthFlow left, OAuthFlow right) {
-    ChangedOAuthFlow changedOAuthFlow = new ChangedOAuthFlow(left, right);
+  public Optional<ChangedOAuthFlow> diff(OAuthFlow left, OAuthFlow right, DiffContext context) {
+    ChangedOAuthFlow changedOAuthFlow = new ChangedOAuthFlow(left, right, context);
     if (left != null && right != null) {
       changedOAuthFlow
           .setAuthorizationUrl(
diff --git a/core/src/main/java/org/openapitools/openapidiff/core/compare/OAuthFlowsDiff.java b/core/src/main/java/org/openapitools/openapidiff/core/compare/OAuthFlowsDiff.java
index 98ceb509f..2b1136618 100644
--- a/core/src/main/java/org/openapitools/openapidiff/core/compare/OAuthFlowsDiff.java
+++ b/core/src/main/java/org/openapitools/openapidiff/core/compare/OAuthFlowsDiff.java
@@ -7,6 +7,7 @@
 import java.util.Map;
 import java.util.Optional;
 import org.openapitools.openapidiff.core.model.ChangedOAuthFlows;
+import org.openapitools.openapidiff.core.model.DiffContext;
 
 public class OAuthFlowsDiff {
   private final OpenApiDiff openApiDiff;
@@ -19,24 +20,24 @@ private static Map<String, Object> getExtensions(OAuthFlows oAuthFlow) {
     return ofNullable(oAuthFlow).map(OAuthFlows::getExtensions).orElse(null);
   }
 
-  public Optional<ChangedOAuthFlows> diff(OAuthFlows left, OAuthFlows right) {
+  public Optional<ChangedOAuthFlows> diff(OAuthFlows left, OAuthFlows right, DiffContext context) {
     ChangedOAuthFlows changedOAuthFlows = new ChangedOAuthFlows(left, right);
     if (left != null && right != null) {
       openApiDiff
           .getOAuthFlowDiff()
-          .diff(left.getImplicit(), right.getImplicit())
+          .diff(left.getImplicit(), right.getImplicit(), context)
           .ifPresent(changedOAuthFlows::setImplicitOAuthFlow);
       openApiDiff
           .getOAuthFlowDiff()
-          .diff(left.getPassword(), right.getPassword())
+          .diff(left.getPassword(), right.getPassword(), context)
           .ifPresent(changedOAuthFlows::setPasswordOAuthFlow);
       openApiDiff
           .getOAuthFlowDiff()
-          .diff(left.getClientCredentials(), right.getClientCredentials())
+          .diff(left.getClientCredentials(), right.getClientCredentials(), context)
           .ifPresent(changedOAuthFlows::setClientCredentialOAuthFlow);
       openApiDiff
           .getOAuthFlowDiff()
-          .diff(left.getAuthorizationCode(), right.getAuthorizationCode())
+          .diff(left.getAuthorizationCode(), right.getAuthorizationCode(), context)
           .ifPresent(changedOAuthFlows::setAuthorizationCodeOAuthFlow);
     }
     openApiDiff
diff --git a/core/src/main/java/org/openapitools/openapidiff/core/compare/OpenApiDiff.java b/core/src/main/java/org/openapitools/openapidiff/core/compare/OpenApiDiff.java
index 1f0fe03bf..c1bb414d8 100644
--- a/core/src/main/java/org/openapitools/openapidiff/core/compare/OpenApiDiff.java
+++ b/core/src/main/java/org/openapitools/openapidiff/core/compare/OpenApiDiff.java
@@ -24,6 +24,7 @@ public class OpenApiDiff {
   private PathDiff pathDiff;
   private SchemaDiff schemaDiff;
   private ContentDiff contentDiff;
+  private MediaTypeDiff mediaTypeDiff;
   private ParametersDiff parametersDiff;
   private ParameterDiff parameterDiff;
   private RequestBodyDiff requestBodyDiff;
@@ -41,6 +42,7 @@ public class OpenApiDiff {
   private MetadataDiff metadataDiff;
   private final OpenAPI oldSpecOpenApi;
   private final OpenAPI newSpecOpenApi;
+  private final OpenApiDiffOptions options;
   private List<Endpoint> newEndpoints;
   private List<Endpoint> missingEndpoints;
   private List<ChangedOperation> changedOperations;
@@ -50,18 +52,24 @@ public class OpenApiDiff {
   /*
    * @param oldSpecOpenApi
    * @param newSpecOpenApi
+   * @param diffOptions
    */
-  private OpenApiDiff(OpenAPI oldSpecOpenApi, OpenAPI newSpecOpenApi) {
+  private OpenApiDiff(OpenAPI oldSpecOpenApi, OpenAPI newSpecOpenApi, OpenApiDiffOptions options) {
     this.oldSpecOpenApi = oldSpecOpenApi;
     this.newSpecOpenApi = newSpecOpenApi;
+    this.options = options;
     if (null == oldSpecOpenApi || null == newSpecOpenApi) {
       throw new RuntimeException("one of the old or new object is null");
     }
+    if (null == options) {
+      throw new IllegalArgumentException("options parameter is null but is required");
+    }
     initializeFields();
   }
 
-  public static ChangedOpenApi compare(OpenAPI oldSpec, OpenAPI newSpec) {
-    return new OpenApiDiff(oldSpec, newSpec).compare();
+  public static ChangedOpenApi compare(
+      OpenAPI oldSpec, OpenAPI newSpec, OpenApiDiffOptions diffOptions) {
+    return new OpenApiDiff(oldSpec, newSpec, diffOptions).compare();
   }
 
   private void initializeFields() {
@@ -69,6 +77,7 @@ private void initializeFields() {
     this.pathDiff = new PathDiff(this);
     this.schemaDiff = new SchemaDiff(this);
     this.contentDiff = new ContentDiff(this);
+    this.mediaTypeDiff = new MediaTypeDiff(this);
     this.parametersDiff = new ParametersDiff(this);
     this.parameterDiff = new ParameterDiff(this);
     this.requestBodyDiff = new RequestBodyDiff(this);
@@ -87,6 +96,10 @@ private void initializeFields() {
     this.deferredSchemaCache = new DeferredSchemaCache(this);
   }
 
+  public OpenApiDiffOptions getOptions() {
+    return options;
+  }
+
   private ChangedOpenApi compare() {
     preProcess(oldSpecOpenApi);
     preProcess(newSpecOpenApi);
@@ -163,7 +176,7 @@ private void preProcess(OpenAPI openApi) {
   }
 
   private ChangedOpenApi getChangedOpenApi() {
-    return new ChangedOpenApi()
+    return new ChangedOpenApi(options)
         .setMissingEndpoints(missingEndpoints)
         .setNewEndpoints(newEndpoints)
         .setNewSpecOpenApi(newSpecOpenApi)
@@ -193,6 +206,10 @@ public ContentDiff getContentDiff() {
     return this.contentDiff;
   }
 
+  public MediaTypeDiff getMediaTypeDiff() {
+    return this.mediaTypeDiff;
+  }
+
   public ParametersDiff getParametersDiff() {
     return this.parametersDiff;
   }
diff --git a/core/src/main/java/org/openapitools/openapidiff/core/compare/OpenApiDiffOptions.java b/core/src/main/java/org/openapitools/openapidiff/core/compare/OpenApiDiffOptions.java
new file mode 100644
index 000000000..23f4e1c37
--- /dev/null
+++ b/core/src/main/java/org/openapitools/openapidiff/core/compare/OpenApiDiffOptions.java
@@ -0,0 +1,49 @@
+package org.openapitools.openapidiff.core.compare;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import org.apache.commons.configuration2.CompositeConfiguration;
+import org.apache.commons.configuration2.YAMLConfiguration;
+import org.apache.commons.configuration2.ex.ConfigurationException;
+
+public class OpenApiDiffOptions {
+  private final CompositeConfiguration config;
+
+  private OpenApiDiffOptions(CompositeConfiguration config) {
+    this.config = config;
+  }
+
+  public CompositeConfiguration getConfig() {
+    return config;
+  }
+
+  public static Builder builder() {
+    return new Builder();
+  }
+
+  public static class Builder {
+    private OpenApiDiffOptions built = new OpenApiDiffOptions(new CompositeConfiguration());
+
+    public Builder configYaml(File file) {
+      YAMLConfiguration yamlConfig = new YAMLConfiguration();
+      try {
+        yamlConfig.read(new FileReader(file));
+      } catch (ConfigurationException | FileNotFoundException e) {
+        throw new IllegalArgumentException("Problem loading config. file=" + file, e);
+      }
+      // Ideally immutable, but since it isn't, we just modify the config directly
+      built.getConfig().addConfigurationFirst(yamlConfig);
+      return this;
+    }
+
+    public Builder configProperty(String propKey, String propVal) {
+      built.getConfig().setProperty(propKey, propVal);
+      return this;
+    }
+
+    public OpenApiDiffOptions build() {
+      return built;
+    }
+  }
+}
diff --git a/core/src/main/java/org/openapitools/openapidiff/core/compare/OperationDiff.java b/core/src/main/java/org/openapitools/openapidiff/core/compare/OperationDiff.java
index a2bc17244..b44093263 100644
--- a/core/src/main/java/org/openapitools/openapidiff/core/compare/OperationDiff.java
+++ b/core/src/main/java/org/openapitools/openapidiff/core/compare/OperationDiff.java
@@ -57,8 +57,7 @@ public DeferredChanged<ChangedOperation> diff(
                 .diff(oldOperation.getOperationId(), newOperation.getOperationId(), context))
         .ifPresent(changedOperation::setOperationId);
     changedOperation.setDeprecated(
-        !Boolean.TRUE.equals(oldOperation.getDeprecated())
-            && Boolean.TRUE.equals(newOperation.getDeprecated()));
+        !Objects.equals(oldOperation.getDeprecated(), newOperation.getDeprecated()));
 
     if (oldOperation.getRequestBody() != null || newOperation.getRequestBody() != null) {
       builder
@@ -72,14 +71,17 @@ public DeferredChanged<ChangedOperation> diff(
           .ifPresent(changedOperation::setRequestBody);
     }
 
+    ParametersDiffResult parametersDiffResult =
+        openApiDiff
+            .getParametersDiff()
+            .diff(oldOperation.getParameters(), newOperation.getParameters(), context);
     builder
-        .with(
-            openApiDiff
-                .getParametersDiff()
-                .diff(oldOperation.getParameters(), newOperation.getParameters(), context))
+        .with(parametersDiffResult.deferredChanged)
         .ifPresent(
             params -> {
-              removePathParameters(context.getParameters(), params);
+              if (!parametersDiffResult.sameOperationsDiffSchema) {
+                removePathParameters(context.getParameters(), params);
+              }
               changedOperation.setParameters(params);
             });
 
diff --git a/core/src/main/java/org/openapitools/openapidiff/core/compare/ParameterDiff.java b/core/src/main/java/org/openapitools/openapidiff/core/compare/ParameterDiff.java
index 6652b0bc4..3a8389d16 100644
--- a/core/src/main/java/org/openapitools/openapidiff/core/compare/ParameterDiff.java
+++ b/core/src/main/java/org/openapitools/openapidiff/core/compare/ParameterDiff.java
@@ -4,8 +4,9 @@
 import io.swagger.v3.oas.models.parameters.Parameter;
 import java.util.HashSet;
 import java.util.Objects;
-import java.util.Optional;
 import org.openapitools.openapidiff.core.model.Changed;
+import org.openapitools.openapidiff.core.model.ChangedExample;
+import org.openapitools.openapidiff.core.model.ChangedExamples;
 import org.openapitools.openapidiff.core.model.ChangedParameter;
 import org.openapitools.openapidiff.core.model.DiffContext;
 import org.openapitools.openapidiff.core.model.deferred.DeferredBuilder;
@@ -49,14 +50,14 @@ protected DeferredChanged<ChangedParameter> computeDiff(
         new ChangedParameter(right.getName(), right.getIn(), context)
             .setOldParameter(left)
             .setNewParameter(right)
-            .setChangeRequired(getBooleanDiff(left.getRequired(), right.getRequired()))
-            .setDeprecated(
-                !Boolean.TRUE.equals(left.getDeprecated())
-                    && Boolean.TRUE.equals(right.getDeprecated()))
+            .setChangeRequired(!Objects.equals(left.getRequired(), right.getRequired()))
+            .setDeprecated(!Objects.equals(left.getDeprecated(), right.getDeprecated()))
             .setChangeAllowEmptyValue(
-                getBooleanDiff(left.getAllowEmptyValue(), right.getAllowEmptyValue()))
+                !Objects.equals(left.getAllowEmptyValue(), right.getAllowEmptyValue()))
             .setChangeStyle(!Objects.equals(left.getStyle(), right.getStyle()))
-            .setChangeExplode(getBooleanDiff(left.getExplode(), right.getExplode()));
+            .setChangeExplode(!Objects.equals(left.getExplode(), right.getExplode()))
+            .setExamples(new ChangedExamples(left.getExamples(), right.getExamples()))
+            .setExample(new ChangedExample(left.getExample(), right.getExample()));
     builder
         .with(
             openApiDiff
@@ -80,10 +81,4 @@ protected DeferredChanged<ChangedParameter> computeDiff(
         .ifPresent(changedParameter::setExtensions);
     return builder.buildIsChanged(changedParameter);
   }
-
-  private boolean getBooleanDiff(Boolean left, Boolean right) {
-    boolean leftRequired = Optional.ofNullable(left).orElse(Boolean.FALSE);
-    boolean rightRequired = Optional.ofNullable(right).orElse(Boolean.FALSE);
-    return leftRequired != rightRequired;
-  }
 }
diff --git a/core/src/main/java/org/openapitools/openapidiff/core/compare/ParametersDiff.java b/core/src/main/java/org/openapitools/openapidiff/core/compare/ParametersDiff.java
index c7fb99b08..6de3b4c28 100644
--- a/core/src/main/java/org/openapitools/openapidiff/core/compare/ParametersDiff.java
+++ b/core/src/main/java/org/openapitools/openapidiff/core/compare/ParametersDiff.java
@@ -3,9 +3,13 @@
 import io.swagger.v3.oas.models.Components;
 import io.swagger.v3.oas.models.parameters.Parameter;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 import java.util.Objects;
 import java.util.Optional;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import org.apache.commons.lang3.StringUtils;
 import org.openapitools.openapidiff.core.model.Changed;
 import org.openapitools.openapidiff.core.model.ChangedParameters;
 import org.openapitools.openapidiff.core.model.DiffContext;
@@ -14,8 +18,19 @@
 import org.openapitools.openapidiff.core.utils.RefPointer;
 import org.openapitools.openapidiff.core.utils.RefType;
 
+class ParametersDiffResult {
+  protected DeferredChanged<ChangedParameters> deferredChanged;
+  protected boolean sameOperationsDiffSchema;
+
+  public ParametersDiffResult(
+      DeferredChanged<ChangedParameters> deferredChanged, boolean sameOperationsDiffSchema) {
+    this.deferredChanged = deferredChanged;
+    this.sameOperationsDiffSchema = sameOperationsDiffSchema;
+  }
+}
 /** compare two parameter */
 public class ParametersDiff {
+
   private static final RefPointer<Parameter> refPointer = new RefPointer<>(RefType.PARAMETERS);
 
   private final Components leftComponents;
@@ -46,31 +61,86 @@ public static boolean same(Parameter left, Parameter right) {
         && Objects.equals(left.getIn(), right.getIn());
   }
 
-  public DeferredChanged<ChangedParameters> diff(
-      List<Parameter> left, List<Parameter> right, DiffContext context) {
+  public ParametersDiffResult diff(
+      final List<Parameter> left, final List<Parameter> right, final DiffContext context) {
+    final DeferredBuilder<Changed> builder = new DeferredBuilder<>();
+    final List<Parameter> wLeft = Optional.ofNullable(left).orElseGet(Collections::emptyList);
+    final List<Parameter> wRight =
+        Optional.ofNullable(right).map(ArrayList::new).orElseGet(ArrayList::new);
 
-    DeferredBuilder<Changed> builder = new DeferredBuilder<>();
-    ChangedParameters changedParameters =
-        new ChangedParameters(left, right != null ? new ArrayList<>(right) : null, context);
-    if (null == left) left = new ArrayList<>();
-    if (null == right) right = new ArrayList<>();
+    final ChangedParameters changedParameters = new ChangedParameters(wLeft, wRight, context);
 
-    for (Parameter leftPara : left) {
-      leftPara = refPointer.resolveRef(leftComponents, leftPara, leftPara.get$ref());
-
-      Optional<Parameter> rightParam = contains(rightComponents, right, leftPara);
-      if (!rightParam.isPresent()) {
-        changedParameters.getMissing().add(leftPara);
+    for (Parameter leftParam : wLeft) {
+      leftParam = refPointer.resolveRef(leftComponents, leftParam, leftParam.get$ref());
+      Optional<Parameter> rightParamOpt = contains(rightComponents, wRight, leftParam);
+      if (!rightParamOpt.isPresent()) {
+        changedParameters.getMissing().add(leftParam);
       } else {
-        Parameter rightPara = rightParam.get();
-        right.remove(rightPara);
+        Parameter rightParam = rightParamOpt.get();
+        wRight.remove(rightParam);
         builder
-            .with(openApiDiff.getParameterDiff().diff(leftPara, rightPara, context))
+            .with(openApiDiff.getParameterDiff().diff(leftParam, rightParam, context))
             .ifPresent(changedParameters.getChanged()::add);
       }
     }
-    changedParameters.getIncreased().addAll(right);
+    changedParameters.getIncreased().addAll(wRight);
+    return new ParametersDiffResult(
+        builder.buildIsChanged(changedParameters),
+        pathUnchangedParametersChanged(changedParameters, context));
+  }
+
+  public boolean pathUnchangedParametersChanged(
+      ChangedParameters changedParameters, DiffContext context) {
+    if (!pathUnchanged(changedParameters, context)) return false;
+    // If missing and increased parameter count is different, it's a new endpoint
+    if (changedParameters.getMissing().size() != changedParameters.getIncreased().size())
+      return false;
+    // Go through each missing Parameter and compare it to newly added Parameters
+    for (Parameter parameter : changedParameters.getMissing()) {
+      // Speedy Check. Use the map already created in changedParameters to check if missing param is
+      // linked to newParam
+      String newParameterName = context.getParameters().get(parameter.getName());
+      if (StringUtils.isBlank(newParameterName)) return false;
+
+      Optional<Parameter> newParameter =
+          changedParameters.getIncreased().stream()
+              .filter(p -> p.getName().equals(newParameterName))
+              .findFirst();
+      if (!newParameter.isPresent()) return false;
 
-    return builder.buildIsChanged(changedParameters);
+      // Check if  the old and new Parameters match . IF so, return TRUE
+      Parameter newParameterRealized = newParameter.get();
+      newParameterRealized.setName(parameter.getName()); // Make names similar
+      boolean samePathDifferentParameter = !newParameterRealized.equals(parameter);
+      newParameterRealized.setName(
+          newParameterName); // Important:: MUST Reset the name as this is not a copy
+      if (samePathDifferentParameter) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  public boolean pathUnchanged(ChangedParameters changedParameters, DiffContext context) {
+    final String REGEX_PATH = "\\{([^/]+)}";
+    String oldUrl = context.getLeftUrl();
+    String newUrl = context.getRightUrl();
+    ArrayList<String> oldUrlPathParams = matchedItems(oldUrl, REGEX_PATH);
+    ArrayList<String> newUrlPathParams = matchedItems(newUrl, REGEX_PATH);
+    // Path Param count doesn't match or param-less path doesn't match or param is changed --> It's
+    // a new endpoint
+    return oldUrlPathParams.size() == newUrlPathParams.size()
+        && changedParameters.getChanged().isEmpty()
+        && oldUrl.replaceAll(REGEX_PATH, "").equals(newUrl.replaceAll(REGEX_PATH, ""));
+  }
+
+  public ArrayList<String> matchedItems(String string, String regex) {
+    Matcher matcher = Pattern.compile(regex).matcher(string);
+    ArrayList<String> matchedItems = new ArrayList<>();
+    while (matcher.find()) {
+      String item = matcher.group();
+      matchedItems.add(item.substring(0, matcher.group().length() - 1).replaceFirst("\\{", ""));
+    }
+    return matchedItems;
   }
 }
diff --git a/core/src/main/java/org/openapitools/openapidiff/core/compare/PathDiff.java b/core/src/main/java/org/openapitools/openapidiff/core/compare/PathDiff.java
index 0537907a0..f695ef083 100644
--- a/core/src/main/java/org/openapitools/openapidiff/core/compare/PathDiff.java
+++ b/core/src/main/java/org/openapitools/openapidiff/core/compare/PathDiff.java
@@ -36,7 +36,12 @@ public DeferredChanged<ChangedPath> diff(PathItem left, PathItem right, DiffCont
           .with(
               openApiDiff
                   .getOperationDiff()
-                  .diff(oldOperation, newOperation, context.copyWithMethod(method)))
+                  .diff(
+                      oldOperation,
+                      newOperation,
+                      context
+                          .copyWithMethod(method)
+                          .copyWithLeftRightUrls(context.getLeftUrl(), context.getRightUrl())))
           .ifPresent(changedPath.getChanged()::add);
     }
     builder
diff --git a/core/src/main/java/org/openapitools/openapidiff/core/compare/PathsDiff.java b/core/src/main/java/org/openapitools/openapidiff/core/compare/PathsDiff.java
index 0b978cdff..8b4f459b8 100644
--- a/core/src/main/java/org/openapitools/openapidiff/core/compare/PathsDiff.java
+++ b/core/src/main/java/org/openapitools/openapidiff/core/compare/PathsDiff.java
@@ -1,10 +1,13 @@
 package org.openapitools.openapidiff.core.compare;
 
+import io.swagger.v3.oas.models.Operation;
 import io.swagger.v3.oas.models.PathItem;
 import io.swagger.v3.oas.models.Paths;
+import io.swagger.v3.oas.models.parameters.Parameter;
 import java.util.*;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
+import java.util.stream.IntStream;
 import org.openapitools.openapidiff.core.model.Changed;
 import org.openapitools.openapidiff.core.model.ChangedPaths;
 import org.openapitools.openapidiff.core.model.DiffContext;
@@ -12,7 +15,7 @@
 import org.openapitools.openapidiff.core.model.deferred.DeferredChanged;
 
 public class PathsDiff {
-  private static final String REGEX_PATH = "\\{([^/]+)}";
+  private static final String REGEX_PATH = "\\{([^/{}]+)}";
   private final OpenApiDiff openApiDiff;
 
   public PathsDiff(OpenApiDiff openApiDiff) {
@@ -37,7 +40,7 @@ public DeferredChanged<ChangedPaths> diff(
       final Map<String, PathItem> left, final Map<String, PathItem> right) {
     DeferredBuilder<Changed> builder = new DeferredBuilder<>();
 
-    ChangedPaths changedPaths = new ChangedPaths(left, right);
+    ChangedPaths changedPaths = new ChangedPaths(left, right, openApiDiff.getOptions());
     changedPaths.getIncreased().putAll(right);
 
     left.keySet()
@@ -50,7 +53,7 @@ public DeferredChanged<ChangedPaths> diff(
                       .filter(item -> normalizePath(item.getKey()).equals(template))
                       .min(
                           (a, b) -> {
-                            if (methodsIntersect(a.getValue(), b.getValue())) {
+                            if (methodsAndParametersIntersect(a.getValue(), b.getValue())) {
                               throw new IllegalArgumentException(
                                   "Two path items have the same signature: " + template);
                             }
@@ -79,9 +82,10 @@ public DeferredChanged<ChangedPaths> diff(
                     params.put(oldParams.get(i), newParams.get(i));
                   }
                 }
-                DiffContext context = new DiffContext();
+                DiffContext context = new DiffContext(openApiDiff.getOptions());
                 context.setUrl(url);
                 context.setParameters(params);
+                context.setLeftAndRightUrls(url, rightUrl);
                 builder
                     .with(openApiDiff.getPathDiff().diff(leftPath, rightPath, context))
                     .ifPresent(path -> changedPaths.getChanged().put(rightUrl, path));
@@ -99,13 +103,54 @@ public static Paths valOrEmpty(Paths path) {
     return path;
   }
 
-  private static boolean methodsIntersect(PathItem a, PathItem b) {
+  /**
+   * @param a a path form the open api spec
+   * @param b another path from the same open api spec
+   * @return <code>true</code> in case both paths are of the same method AND their templated
+   *     parameters are of the same type; <code>false</code> otherwise
+   */
+  private static boolean methodsAndParametersIntersect(PathItem a, PathItem b) {
     Set<PathItem.HttpMethod> methodsA = a.readOperationsMap().keySet();
     for (PathItem.HttpMethod method : b.readOperationsMap().keySet()) {
       if (methodsA.contains(method)) {
-        return true;
+        Operation left = a.readOperationsMap().get(method);
+        Operation right = b.readOperationsMap().get(method);
+        if (left.getParameters().size() == right.getParameters().size()) {
+          return parametersIntersect(left.getParameters(), right.getParameters());
+        }
+        return false;
       }
     }
     return false;
   }
+
+  /**
+   * Checks if provided parameter pairs are equal by type and format
+   *
+   * @param left parameters from the first compared method
+   * @param right parameters from the second compared method
+   * @return <code>true</code> in case each parameter pair is of the same type; <code>false</code>
+   *     otherwise
+   */
+  private static boolean parametersIntersect(List<Parameter> left, List<Parameter> right) {
+    int parametersSize = left.size();
+    long intersectedParameters =
+        IntStream.range(0, left.size())
+            .filter(i -> parametersTypeEquals(left.get(i), right.get(i)))
+            .count();
+    return parametersSize == intersectedParameters;
+  }
+
+  /**
+   * Checks if provided parameter pair is equal by type and format
+   *
+   * @param left parameter from the first compared method
+   * @param right parameter from the second compared method
+   * @return <code>true</code> in case parameter pair is of the same type; <code>false</code>
+   *     otherwise
+   */
+  private static boolean parametersTypeEquals(Parameter left, Parameter right) {
+    return Objects.equals(left.getSchema().getType(), right.getSchema().getType())
+        && Objects.equals(left.getSchema().getFormat(), right.getSchema().getFormat());
+  }
 }
diff --git a/core/src/main/java/org/openapitools/openapidiff/core/compare/SchemaDiff.java b/core/src/main/java/org/openapitools/openapidiff/core/compare/SchemaDiff.java
index 822160ede..1b410818b 100644
--- a/core/src/main/java/org/openapitools/openapidiff/core/compare/SchemaDiff.java
+++ b/core/src/main/java/org/openapitools/openapidiff/core/compare/SchemaDiff.java
@@ -10,11 +10,13 @@
 import io.swagger.v3.oas.models.media.Schema;
 import io.swagger.v3.oas.models.media.XML;
 import java.util.ArrayList;
+import java.util.HashSet;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
 import java.util.Optional;
+import java.util.Set;
 import org.openapitools.openapidiff.core.compare.schemadiffresult.ArraySchemaDiffResult;
 import org.openapitools.openapidiff.core.compare.schemadiffresult.ComposedSchemaDiffResult;
 import org.openapitools.openapidiff.core.compare.schemadiffresult.SchemaDiffResult;
@@ -79,23 +81,32 @@ public static SchemaDiffResult getSchemaDiffResult(
     }
   }
 
-  protected static Schema<?> resolveComposedSchema(Components components, Schema<?> schema) {
+  protected static Schema<?> resolveComposedSchema(
+      Components components, Schema<?> schema, Set<String> visitedRefs) {
     if (schema instanceof ComposedSchema) {
       ComposedSchema composedSchema = (ComposedSchema) schema;
-      List<Schema> allOfSchemaList = composedSchema.getAllOf();
-      if (allOfSchemaList != null) {
-        for (Schema<?> allOfSchema : allOfSchemaList) {
-          allOfSchema = refPointer.resolveRef(components, allOfSchema, allOfSchema.get$ref());
-          allOfSchema = resolveComposedSchema(components, allOfSchema);
-          schema = addSchema(schema, allOfSchema);
+      List<Schema> composedSchemas = new ArrayList<>();
+      Optional.ofNullable(composedSchema.getAllOf()).ifPresent(composedSchemas::addAll);
+      Optional.ofNullable(composedSchema.getAnyOf()).ifPresent(composedSchemas::addAll);
+
+      if (!composedSchemas.isEmpty()) {
+        for (Schema<?> composed : composedSchemas) {
+          if (composed.get$ref() == null || !visitedRefs.contains(composed.get$ref())) {
+            Set<String> updatedVisitedRefs = new HashSet<>(visitedRefs);
+            updatedVisitedRefs.add(composed.get$ref());
+            composed = refPointer.resolveRef(components, composed, composed.get$ref());
+            composed = resolveComposedSchema(components, composed, updatedVisitedRefs);
+            schema = addSchema(schema, composed);
+          }
         }
         composedSchema.setAllOf(null);
+        composedSchema.setAnyOf(null);
       }
     }
     return schema;
   }
 
-  protected static Schema<?> addSchema(Schema<?> schema, Schema<?> fromSchema) {
+  protected static <T, U> Schema<T> addSchema(Schema<T> schema, Schema<U> fromSchema) {
     if (fromSchema.getProperties() != null) {
       if (schema.getProperties() == null) {
         schema.setProperties(new LinkedHashMap<>());
@@ -154,6 +165,16 @@ protected static Schema<?> addSchema(Schema<?> schema, Schema<?> fromSchema) {
       }
       schema.getExtensions().putAll(fromSchema.getExtensions());
     }
+    if (fromSchema instanceof ComposedSchema && schema instanceof ComposedSchema) {
+      ComposedSchema composedFromSchema = (ComposedSchema) fromSchema;
+      ComposedSchema composedSchema = (ComposedSchema) schema;
+      if (composedFromSchema.getOneOf() != null) {
+        if (composedSchema.getOneOf() == null) {
+          composedSchema.setOneOf(new ArrayList<>());
+        }
+        composedSchema.getOneOf().addAll(composedFromSchema.getOneOf());
+      }
+    }
     if (fromSchema.getDiscriminator() != null) {
       if (schema.getDiscriminator() == null) {
         schema.setDiscriminator(new Discriminator());
@@ -185,6 +206,9 @@ protected static Schema<?> addSchema(Schema<?> schema, Schema<?> fromSchema) {
     if (fromSchema.getExample() != null) {
       schema.setExample(fromSchema.getExample());
     }
+    if (fromSchema.getExamples() != null) {
+      schema.setExamples((List<T>) fromSchema.getExamples());
+    }
     if (fromSchema.getExternalDocs() != null) {
       if (schema.getExternalDocs() == null) {
         schema.setExternalDocs(new ExternalDocumentation());
@@ -316,8 +340,8 @@ public DeferredChanged<ChangedSchema> computeDiffForReal(
     left = refPointer.resolveRef(this.leftComponents, left, getSchemaRef(left));
     right = refPointer.resolveRef(this.rightComponents, right, getSchemaRef(right));
 
-    left = resolveComposedSchema(leftComponents, left);
-    right = resolveComposedSchema(rightComponents, right);
+    left = resolveComposedSchema(leftComponents, left, new HashSet<>());
+    right = resolveComposedSchema(rightComponents, right, new HashSet<>());
 
     // If type of schemas are different, just set old & new schema, set changedType to true in
     // SchemaDiffResult and
diff --git a/core/src/main/java/org/openapitools/openapidiff/core/compare/SecurityRequirementDiff.java b/core/src/main/java/org/openapitools/openapidiff/core/compare/SecurityRequirementDiff.java
index 237e7815f..76b51a5d0 100644
--- a/core/src/main/java/org/openapitools/openapidiff/core/compare/SecurityRequirementDiff.java
+++ b/core/src/main/java/org/openapitools/openapidiff/core/compare/SecurityRequirementDiff.java
@@ -66,7 +66,7 @@ public DeferredChanged<ChangedSecurityRequirement> diff(
     DeferredBuilder<Changed> builder = new DeferredBuilder<>();
 
     ChangedSecurityRequirement changedSecurityRequirement =
-        new ChangedSecurityRequirement(left, right != null ? getCopy(right) : null);
+        new ChangedSecurityRequirement(left, right != null ? getCopy(right) : null, context);
 
     SecurityRequirement leftRequirement = left == null ? new SecurityRequirement() : left;
     SecurityRequirement rightRequirement = right == null ? new SecurityRequirement() : right;
diff --git a/core/src/main/java/org/openapitools/openapidiff/core/compare/SecurityRequirementsDiff.java b/core/src/main/java/org/openapitools/openapidiff/core/compare/SecurityRequirementsDiff.java
index 291dd5c65..324bf0017 100644
--- a/core/src/main/java/org/openapitools/openapidiff/core/compare/SecurityRequirementsDiff.java
+++ b/core/src/main/java/org/openapitools/openapidiff/core/compare/SecurityRequirementsDiff.java
@@ -88,7 +88,7 @@ protected DeferredChanged<ChangedSecurityRequirements> diff(
     right = right == null ? new ArrayList<>() : getCopy(right);
 
     ChangedSecurityRequirements changedSecurityRequirements =
-        new ChangedSecurityRequirements(left, right);
+        new ChangedSecurityRequirements(left, right, context);
 
     for (SecurityRequirement leftSecurity : left) {
       Optional<SecurityRequirement> rightSecOpt = contains(right, leftSecurity);
diff --git a/core/src/main/java/org/openapitools/openapidiff/core/compare/SecuritySchemeDiff.java b/core/src/main/java/org/openapitools/openapidiff/core/compare/SecuritySchemeDiff.java
index a27edb374..f68d638d3 100644
--- a/core/src/main/java/org/openapitools/openapidiff/core/compare/SecuritySchemeDiff.java
+++ b/core/src/main/java/org/openapitools/openapidiff/core/compare/SecuritySchemeDiff.java
@@ -51,8 +51,8 @@ public DeferredChanged<ChangedSecurityScheme> diff(
         changedSecuritySchemeOptional -> {
           ChangedSecurityScheme changedSecurityScheme =
               changedSecuritySchemeOptional.orElse(
-                  new ChangedSecurityScheme(leftSecurityScheme, rightSecurityScheme));
-          changedSecurityScheme = getCopyWithoutScopes(changedSecurityScheme);
+                  new ChangedSecurityScheme(leftSecurityScheme, rightSecurityScheme, context));
+          changedSecurityScheme = getCopyWithoutScopes(changedSecurityScheme, context);
 
           if (changedSecurityScheme != null
               && leftSecurityScheme.getType() == SecurityScheme.Type.OAUTH2) {
@@ -71,7 +71,7 @@ protected DeferredChanged<ChangedSecurityScheme> computeDiff(
       SecurityScheme rightSecurityScheme,
       DiffContext context) {
     ChangedSecurityScheme changedSecurityScheme =
-        new ChangedSecurityScheme(leftSecurityScheme, rightSecurityScheme);
+        new ChangedSecurityScheme(leftSecurityScheme, rightSecurityScheme, context);
 
     openApiDiff
         .getMetadataDiff()
@@ -87,7 +87,7 @@ protected DeferredChanged<ChangedSecurityScheme> computeDiff(
       case OAUTH2:
         openApiDiff
             .getOAuthFlowsDiff()
-            .diff(leftSecurityScheme.getFlows(), rightSecurityScheme.getFlows())
+            .diff(leftSecurityScheme.getFlows(), rightSecurityScheme.getFlows(), context)
             .ifPresent(changedSecurityScheme::setOAuthFlows);
         break;
 
@@ -114,9 +114,10 @@ protected DeferredChanged<ChangedSecurityScheme> computeDiff(
     return new RealizedChanged<>(changedSecurityScheme);
   }
 
-  private ChangedSecurityScheme getCopyWithoutScopes(ChangedSecurityScheme original) {
+  private ChangedSecurityScheme getCopyWithoutScopes(
+      ChangedSecurityScheme original, DiffContext context) {
     return new ChangedSecurityScheme(
-            original.getOldSecurityScheme(), original.getNewSecurityScheme())
+            original.getOldSecurityScheme(), original.getNewSecurityScheme(), context)
         .setChangedType(original.isChangedType())
         .setChangedIn(original.isChangedIn())
         .setChangedScheme(original.isChangedScheme())
diff --git a/core/src/main/java/org/openapitools/openapidiff/core/compare/schemadiffresult/ComposedSchemaDiffResult.java b/core/src/main/java/org/openapitools/openapidiff/core/compare/schemadiffresult/ComposedSchemaDiffResult.java
index e76aa0155..2f86318eb 100644
--- a/core/src/main/java/org/openapitools/openapidiff/core/compare/schemadiffresult/ComposedSchemaDiffResult.java
+++ b/core/src/main/java/org/openapitools/openapidiff/core/compare/schemadiffresult/ComposedSchemaDiffResult.java
@@ -12,13 +12,13 @@
 import org.apache.commons.collections4.CollectionUtils;
 import org.openapitools.openapidiff.core.compare.MapKeyDiff;
 import org.openapitools.openapidiff.core.compare.OpenApiDiff;
-import org.openapitools.openapidiff.core.model.ChangedOneOfSchema;
 import org.openapitools.openapidiff.core.model.ChangedSchema;
 import org.openapitools.openapidiff.core.model.DiffContext;
 import org.openapitools.openapidiff.core.model.deferred.DeferredBuilder;
 import org.openapitools.openapidiff.core.model.deferred.DeferredChanged;
 import org.openapitools.openapidiff.core.model.deferred.RealizedChanged;
 import org.openapitools.openapidiff.core.model.deferred.RecursiveSchemaSet;
+import org.openapitools.openapidiff.core.model.schema.ChangedOneOfSchema;
 import org.openapitools.openapidiff.core.utils.RefPointer;
 import org.openapitools.openapidiff.core.utils.RefType;
 
diff --git a/core/src/main/java/org/openapitools/openapidiff/core/compare/schemadiffresult/SchemaDiffResult.java b/core/src/main/java/org/openapitools/openapidiff/core/compare/schemadiffresult/SchemaDiffResult.java
index 540188ecb..32cb5bee9 100644
--- a/core/src/main/java/org/openapitools/openapidiff/core/compare/schemadiffresult/SchemaDiffResult.java
+++ b/core/src/main/java/org/openapitools/openapidiff/core/compare/schemadiffresult/SchemaDiffResult.java
@@ -11,12 +11,16 @@
 import org.openapitools.openapidiff.core.compare.OpenApiDiff;
 import org.openapitools.openapidiff.core.model.Change;
 import org.openapitools.openapidiff.core.model.Changed;
+import org.openapitools.openapidiff.core.model.ChangedExample;
+import org.openapitools.openapidiff.core.model.ChangedExamples;
 import org.openapitools.openapidiff.core.model.ChangedSchema;
 import org.openapitools.openapidiff.core.model.DiffContext;
 import org.openapitools.openapidiff.core.model.deferred.DeferredBuilder;
 import org.openapitools.openapidiff.core.model.deferred.DeferredChanged;
 import org.openapitools.openapidiff.core.model.deferred.RecursiveSchemaSet;
 import org.openapitools.openapidiff.core.model.schema.*;
+import org.openapitools.openapidiff.core.model.schema.ChangedMaxItems;
+import org.openapitools.openapidiff.core.model.schema.ChangedMinItems;
 
 public class SchemaDiffResult {
   protected ChangedSchema changedSchema;
@@ -48,9 +52,7 @@ public <V extends Schema<X>, X> DeferredChanged<ChangedSchema> diff(
         .setContext(context)
         .setOldSchema(left)
         .setNewSchema(right)
-        .setChangeDeprecated(
-            !Boolean.TRUE.equals(left.getDeprecated())
-                && Boolean.TRUE.equals(right.getDeprecated()))
+        .setChangeDeprecated(!Objects.equals(left.getDeprecated(), right.getDeprecated()))
         .setChangeTitle(!Objects.equals(left.getTitle(), right.getTitle()))
         .setRequired(
             ListDiff.diff(new ChangedRequired(left.getRequired(), right.getRequired(), context)))
@@ -59,7 +61,29 @@ public <V extends Schema<X>, X> DeferredChanged<ChangedSchema> diff(
         .setChangeFormat(!Objects.equals(left.getFormat(), right.getFormat()))
         .setReadOnly(new ChangedReadOnly(left.getReadOnly(), right.getReadOnly(), context))
         .setWriteOnly(new ChangedWriteOnly(left.getWriteOnly(), right.getWriteOnly(), context))
-        .setMaxLength(new ChangedMaxLength(left.getMaxLength(), right.getMaxLength(), context));
+        .setMaxLength(new ChangedMaxLength(left.getMaxLength(), right.getMaxLength(), context))
+        .setNumericRange(
+            new ChangedNumericRange(
+                left.getMinimum(),
+                right.getMinimum(),
+                left.getMaximum(),
+                right.getMaximum(),
+                left.getExclusiveMinimum(),
+                right.getExclusiveMinimum(),
+                left.getExclusiveMaximum(),
+                right.getExclusiveMaximum(),
+                context))
+        .setMultipleOf(new ChangedMultipleOf(left.getMultipleOf(), right.getMultipleOf()))
+        .setNullable(new ChangedNullable(left.getNullable(), right.getNullable()))
+        .setUniqueItems(new ChangedUniqueItems(left.getUniqueItems(), right.getUniqueItems()))
+        .setExamples(new ChangedExamples(left.getExamples(), right.getExamples()))
+        .setExample(new ChangedExample(left.getExample(), right.getExample()))
+        .setMaxItems(new ChangedMaxItems(left.getMaxItems(), right.getMaxItems(), context))
+        .setMinItems(new ChangedMinItems(left.getMinItems(), right.getMinItems(), context))
+        .setMaxProperties(
+            new ChangedMaxProperties(left.getMaxProperties(), right.getMaxProperties(), context))
+        .setMinProperties(
+            new ChangedMinProperties(left.getMinProperties(), right.getMinProperties(), context));
     builder
         .with(
             openApiDiff
diff --git a/core/src/main/java/org/openapitools/openapidiff/core/exception/RendererException.java b/core/src/main/java/org/openapitools/openapidiff/core/exception/RendererException.java
new file mode 100644
index 000000000..d7dc24447
--- /dev/null
+++ b/core/src/main/java/org/openapitools/openapidiff/core/exception/RendererException.java
@@ -0,0 +1,12 @@
+package org.openapitools.openapidiff.core.exception;
+
+public class RendererException extends RuntimeException {
+
+  public RendererException(Throwable cause) {
+    super(cause);
+  }
+
+  public RendererException(String message, Throwable cause) {
+    super(message, cause);
+  }
+}
diff --git a/core/src/main/java/org/openapitools/openapidiff/core/model/BackwardIncompatibleProp.java b/core/src/main/java/org/openapitools/openapidiff/core/model/BackwardIncompatibleProp.java
new file mode 100644
index 000000000..f05a72f75
--- /dev/null
+++ b/core/src/main/java/org/openapitools/openapidiff/core/model/BackwardIncompatibleProp.java
@@ -0,0 +1,82 @@
+package org.openapitools.openapidiff.core.model;
+
+import static org.apache.commons.lang3.ArrayUtils.isEmpty;
+
+import org.apache.commons.configuration2.Configuration;
+
+// Properties to enable/disable backward incompatibility checks
+// Note: order is not programmatically significant but keep alphabetical for maintainability
+public enum BackwardIncompatibleProp {
+  EXTENSION_CONTENT_TYPES_DECREASED("incompatible.extension.content.types.decreased", false),
+  EXTENSION_CONTENT_TYPE_DELETED("incompatible.extension.content.type.%s.deleted", false),
+  OPENAPI_ENDPOINTS_DECREASED("incompatible.openapi.endpoints.decreased", true),
+  REQUEST_BODY_REQUIRED_INCREASED("incompatible.request.body.increased", true),
+  REQUEST_CONTENT_DECREASED("incompatible.request.content.decreased", true),
+  REQUEST_ENUM_DECREASED("incompatible.request.enum.decreased", true),
+  REQUEST_MAX_LENGTH_DECREASED("incompatible.request.max.length.decreased", true),
+  REQUEST_NUMERIC_RANGE_DECREASED("incompatible.request.numeric.range.decreased", true),
+  REQUEST_ONEOF_DECREASED("incompatible.request.oneof.decreased", true),
+  REQUEST_PARAM_ALLOWEMPTY_DECREASED("incompatible.request.param.allowempty.decreased", true),
+  REQUEST_PARAM_EXPLODE_CHANGED("incompatible.request.param.explode.changed", true),
+  REQUEST_PARAM_STYLE_CHANGED("incompatible.request.param.style.changed", true),
+  REQUEST_PARAMS_DECREASED("incompatible.request.params.decreased", true),
+  REQUEST_PARAMS_REQUIRED_INCREASED("incompatible.request.params.required.increased", true),
+  REQUEST_READONLY_INCREASED("incompatible.request.readonly.increased", true),
+  REQUEST_READONLY_REQUIRED_DECREASED("incompatible.request.readonly.required.decreased", true),
+  REQUEST_REQUIRED_INCREASED("incompatible.request.required.increased", true),
+  RESPONSE_CONTENT_DECREASED("incompatible.response.content.decreased", true),
+  RESPONSE_ENUM_INCREASED("incompatible.response.enum.increased", true),
+  RESPONSE_HEADER_EXPLODE_CHANGED("incompatible.response.header.explode.changed", true),
+  RESPONSE_HEADER_REQUIRED_DECREASED("incompatible.response.header.required.decreased", true),
+  RESPONSE_HEADER_REQUIRED_INCREASED("incompatible.response.header.required.increased", true),
+  RESPONSE_HEADERS_DECREASED("incompatible.response.headers.decreased", true),
+  RESPONSE_MAX_LENGTH_INCREASED("incompatible.response.max.length.increased", true),
+  RESPONSE_NUMERIC_RANGE_INCREASED("incompatible.response.numeric.range.increased", false),
+  RESPONSE_ONEOF_INCREASED("incompatible.response.oneof.increased", true),
+  RESPONSE_REQUIRED_DECREASED("incompatible.response.required.decreased", true),
+  RESPONSE_RESPONSES_DECREASED("incompatible.response.responses.decreased", true),
+  RESPONSE_WRITEONLY_INCREASED("incompatible.response.writeonly.increased", true),
+  RESPONSE_WRITEONLY_REQUIRED_DECREASED("incompatible.response.writeonly.required.decreased", true),
+  SECURITY_REQUIREMENT_SCHEMES_INCREASED(
+      "incompatible.security.requirement.schemes.increased", true),
+  SECURITY_REQUIREMENTS_DECREASED("incompatible.security.requirements.decreased", true),
+  SECURITY_SCHEME_BEARER_FORMAT_CHANGED("incompatible.security.scheme.bearer.format.changed", true),
+  SECURITY_SCHEME_OAUTH2_AUTH_URL_CHANGED(
+      "incompatible.security.scheme.oauth2.auth.url.changed", true),
+  SECURITY_SCHEME_OAUTH2_REFRESH_URL_CHANGED(
+      "incompatible.security.scheme.oauth2.refresh.url.changed", true),
+  SECURITY_SCHEME_OAUTH2_TOKEN_URL_CHANGED(
+      "incompatible.security.scheme.oauth2.token.url.changed", true),
+  SECURITY_SCHEME_OPENIDCONNECT_URL_CHANGED(
+      "incompatible.security.scheme.openidconnect.url.changed", true),
+  SECURITY_SCHEME_SCHEME_CHANGED("incompatible.security.scheme.scheme.changed", true),
+  SECURITY_SCHEME_SCOPES_INCREASED("incompatible.security.scheme.scopes.increased", true),
+  SCHEMA_DISCRIMINATOR_CHANGED("incompatible.schema.discriminator.changed", true),
+  SCHEMA_TYPE_CHANGED("incompatible.schema.type.changed", true),
+  ;
+
+  private final String propertyName;
+  private final boolean enabledByDefault;
+
+  BackwardIncompatibleProp(String propertyName, boolean enabledByDefault) {
+    this.propertyName = propertyName;
+    this.enabledByDefault = enabledByDefault;
+  }
+
+  public String getPropertyName() {
+    return propertyName;
+  }
+
+  public boolean isEnabledByDefault() {
+    return enabledByDefault;
+  }
+
+  public boolean enabled(DiffContext context, Object... formatArgs) {
+    return enabled(context.getConfig(), formatArgs);
+  }
+
+  public boolean enabled(Configuration cfg, Object... formatArgs) {
+    String propName = isEmpty(formatArgs) ? propertyName : String.format(propertyName, formatArgs);
+    return cfg.getBoolean(propName, enabledByDefault);
+  }
+}
diff --git a/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedApiResponse.java b/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedApiResponse.java
index e496f7a71..0b12a0ead 100644
--- a/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedApiResponse.java
+++ b/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedApiResponse.java
@@ -1,5 +1,7 @@
 package org.openapitools.openapidiff.core.model;
 
+import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.RESPONSE_RESPONSES_DECREASED;
+
 import io.swagger.v3.oas.models.responses.ApiResponse;
 import io.swagger.v3.oas.models.responses.ApiResponses;
 import java.util.LinkedHashMap;
@@ -39,10 +41,12 @@ public DiffResult isCoreChanged() {
     if (increased.isEmpty() && missing.isEmpty()) {
       return DiffResult.NO_CHANGES;
     }
-    if (!increased.isEmpty() && missing.isEmpty()) {
-      return DiffResult.COMPATIBLE;
+    if (!missing.isEmpty()) {
+      if (RESPONSE_RESPONSES_DECREASED.enabled(context)) {
+        return DiffResult.INCOMPATIBLE;
+      }
     }
-    return DiffResult.INCOMPATIBLE;
+    return DiffResult.COMPATIBLE;
   }
 
   public ApiResponses getOldApiResponses() {
diff --git a/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedContent.java b/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedContent.java
index 5bc6ddf2a..472803ffd 100644
--- a/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedContent.java
+++ b/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedContent.java
@@ -1,5 +1,8 @@
 package org.openapitools.openapidiff.core.model;
 
+import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.REQUEST_CONTENT_DECREASED;
+import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.RESPONSE_CONTENT_DECREASED;
+
 import io.swagger.v3.oas.models.media.Content;
 import io.swagger.v3.oas.models.media.MediaType;
 import java.util.*;
@@ -31,10 +34,15 @@ public DiffResult isCoreChanged() {
     if (increased.isEmpty() && missing.isEmpty()) {
       return DiffResult.NO_CHANGES;
     }
-    if (context.isRequest() && missing.isEmpty() || context.isResponse() && increased.isEmpty()) {
-      return DiffResult.COMPATIBLE;
+    if (!missing.isEmpty()) {
+      if (context.isRequest() && REQUEST_CONTENT_DECREASED.enabled(context)) {
+        return DiffResult.INCOMPATIBLE;
+      }
+      if (context.isResponse() && RESPONSE_CONTENT_DECREASED.enabled(context)) {
+        return DiffResult.INCOMPATIBLE;
+      }
     }
-    return DiffResult.INCOMPATIBLE;
+    return DiffResult.COMPATIBLE;
   }
 
   public Content getOldContent() {
diff --git a/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedExample.java b/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedExample.java
new file mode 100644
index 000000000..4e540eca3
--- /dev/null
+++ b/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedExample.java
@@ -0,0 +1,66 @@
+package org.openapitools.openapidiff.core.model;
+
+import java.util.Objects;
+
+public class ChangedExample implements Changed {
+
+  private Object leftExample;
+  private Object rightExample;
+
+  public ChangedExample(Object leftExample, Object rightExample) {
+    this.leftExample = leftExample;
+    this.rightExample = rightExample;
+  }
+
+  public Object getLeftExample() {
+    return leftExample;
+  }
+
+  public void setLeftExample(Object leftExample) {
+    this.leftExample = leftExample;
+  }
+
+  public Object getRightExample() {
+    return rightExample;
+  }
+
+  public void setRightExample(Object rightExample) {
+    this.rightExample = rightExample;
+  }
+
+  @Override
+  public DiffResult isChanged() {
+    if (!Objects.equals(leftExample, rightExample)) {
+      return DiffResult.METADATA;
+    }
+    return DiffResult.NO_CHANGES;
+  }
+
+  @Override
+  public boolean equals(Object object) {
+    if (this == object) {
+      return true;
+    }
+    if (object == null || getClass() != object.getClass()) {
+      return false;
+    }
+    ChangedExample that = (ChangedExample) object;
+    return Objects.equals(leftExample, that.leftExample)
+        && Objects.equals(rightExample, that.rightExample);
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hash(leftExample, rightExample);
+  }
+
+  @Override
+  public String toString() {
+    return "ChangedExample{"
+        + "leftExample="
+        + leftExample
+        + ", rightExample="
+        + rightExample
+        + '}';
+  }
+}
diff --git a/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedExamples.java b/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedExamples.java
new file mode 100644
index 000000000..c1a00bbbc
--- /dev/null
+++ b/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedExamples.java
@@ -0,0 +1,66 @@
+package org.openapitools.openapidiff.core.model;
+
+import java.util.Objects;
+
+public class ChangedExamples implements Changed {
+
+  private Object leftExamples;
+  private Object rightExamples;
+
+  public ChangedExamples(Object leftExamples, Object rightExamples) {
+    this.leftExamples = leftExamples;
+    this.rightExamples = rightExamples;
+  }
+
+  public Object getLeftExamples() {
+    return leftExamples;
+  }
+
+  public void setLeftExamples(Object leftExamples) {
+    this.leftExamples = leftExamples;
+  }
+
+  public Object getRightExamples() {
+    return rightExamples;
+  }
+
+  public void setRightExamples(Object rightExamples) {
+    this.rightExamples = rightExamples;
+  }
+
+  @Override
+  public DiffResult isChanged() {
+    if (!Objects.equals(leftExamples, rightExamples)) {
+      return DiffResult.METADATA;
+    }
+    return DiffResult.NO_CHANGES;
+  }
+
+  @Override
+  public boolean equals(Object object) {
+    if (this == object) {
+      return true;
+    }
+    if (object == null || getClass() != object.getClass()) {
+      return false;
+    }
+    ChangedExamples that = (ChangedExamples) object;
+    return Objects.equals(leftExamples, that.leftExamples)
+        && Objects.equals(rightExamples, that.rightExamples);
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hash(leftExamples, rightExamples);
+  }
+
+  @Override
+  public String toString() {
+    return "ChangedExamples{"
+        + ", leftExamples="
+        + leftExamples
+        + ", rightExamples="
+        + rightExamples
+        + '}';
+  }
+}
diff --git a/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedExtensions.java b/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedExtensions.java
index cde3b1511..f189b3e8c 100644
--- a/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedExtensions.java
+++ b/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedExtensions.java
@@ -1,5 +1,8 @@
 package org.openapitools.openapidiff.core.model;
 
+import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.EXTENSION_CONTENT_TYPES_DECREASED;
+import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.EXTENSION_CONTENT_TYPE_DELETED;
+
 import java.util.*;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
@@ -32,7 +35,20 @@ public List<Changed> getChangedElements() {
 
   @Override
   public DiffResult isCoreChanged() {
-    return DiffResult.NO_CHANGES;
+    if (increased.isEmpty() && missing.isEmpty()) {
+      return DiffResult.NO_CHANGES;
+    }
+    if (!missing.isEmpty()) {
+      if (EXTENSION_CONTENT_TYPES_DECREASED.enabled(context)) {
+        return DiffResult.INCOMPATIBLE;
+      }
+      for (String key : missing.keySet()) {
+        if (EXTENSION_CONTENT_TYPE_DELETED.enabled(context, key)) {
+          return DiffResult.INCOMPATIBLE;
+        }
+      }
+    }
+    return DiffResult.COMPATIBLE;
   }
 
   public Map<String, Object> getOldExtensions() {
diff --git a/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedHeader.java b/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedHeader.java
index 8ec56568f..0c9e3384f 100644
--- a/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedHeader.java
+++ b/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedHeader.java
@@ -1,5 +1,9 @@
 package org.openapitools.openapidiff.core.model;
 
+import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.RESPONSE_HEADER_EXPLODE_CHANGED;
+import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.RESPONSE_HEADER_REQUIRED_DECREASED;
+import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.RESPONSE_HEADER_REQUIRED_INCREASED;
+
 import io.swagger.v3.oas.models.headers.Header;
 import java.util.Arrays;
 import java.util.List;
@@ -14,6 +18,8 @@ public class ChangedHeader implements ComposedChanged {
   private boolean style;
   private boolean explode;
   private ChangedMetadata description;
+  private ChangedExamples examples;
+  private ChangedExample example;
   private ChangedSchema schema;
   private ChangedContent content;
   private ChangedExtensions extensions;
@@ -26,7 +32,7 @@ public ChangedHeader(Header oldHeader, Header newHeader, DiffContext context) {
 
   @Override
   public List<Changed> getChangedElements() {
-    return Arrays.asList(description, schema, content, extensions);
+    return Arrays.asList(description, example, examples, schema, content, extensions);
   }
 
   @Override
@@ -34,10 +40,30 @@ public DiffResult isCoreChanged() {
     if (!required && !deprecated && !style && !explode) {
       return DiffResult.NO_CHANGES;
     }
-    if (!required && !style && !explode) {
-      return DiffResult.COMPATIBLE;
+    if (explode) {
+      if (RESPONSE_HEADER_EXPLODE_CHANGED.enabled(context)) {
+        return DiffResult.INCOMPATIBLE;
+      }
+    }
+    if (required) {
+      boolean requiredOld = oldHeader.getRequired() != null ? oldHeader.getRequired() : false;
+      boolean requiredNew = newHeader.getRequired() != null ? newHeader.getRequired() : false;
+      if (requiredOld && !requiredNew) {
+        if (RESPONSE_HEADER_REQUIRED_DECREASED.enabled(context)) {
+          return DiffResult.INCOMPATIBLE;
+        }
+      }
+      if (!requiredOld && requiredNew) {
+        // TODO: Document why desired or remove support. Client will just ignore new header?
+        if (RESPONSE_HEADER_REQUIRED_INCREASED.enabled(context)) {
+          return DiffResult.INCOMPATIBLE;
+        }
+      }
     }
-    return DiffResult.INCOMPATIBLE;
+    if (style) {
+      return DiffResult.INCOMPATIBLE;
+    }
+    return DiffResult.COMPATIBLE;
   }
 
   public Header getOldHeader() {
@@ -72,6 +98,14 @@ public ChangedMetadata getDescription() {
     return this.description;
   }
 
+  public ChangedExamples getExamples() {
+    return this.examples;
+  }
+
+  public ChangedExample getExample() {
+    return this.example;
+  }
+
   public ChangedSchema getSchema() {
     return this.schema;
   }
@@ -109,6 +143,16 @@ public ChangedHeader setDescription(final ChangedMetadata description) {
     return this;
   }
 
+  public ChangedHeader setExamples(ChangedExamples examples) {
+    this.examples = examples;
+    return this;
+  }
+
+  public ChangedHeader setExample(ChangedExample example) {
+    this.example = example;
+    return this;
+  }
+
   public ChangedHeader setSchema(final ChangedSchema schema) {
     this.schema = schema;
     return this;
@@ -137,6 +181,8 @@ public boolean equals(Object o) {
         && Objects.equals(newHeader, that.newHeader)
         && Objects.equals(context, that.context)
         && Objects.equals(description, that.description)
+        && Objects.equals(examples, that.examples)
+        && Objects.equals(example, that.example)
         && Objects.equals(schema, that.schema)
         && Objects.equals(content, that.content)
         && Objects.equals(extensions, that.extensions);
@@ -153,6 +199,8 @@ public int hashCode() {
         style,
         explode,
         description,
+        examples,
+        example,
         schema,
         content,
         extensions);
@@ -176,6 +224,10 @@ public java.lang.String toString() {
         + this.isExplode()
         + ", description="
         + this.getDescription()
+        + ", examples="
+        + this.getExamples()
+        + ", example="
+        + this.getExample()
         + ", schema="
         + this.getSchema()
         + ", content="
diff --git a/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedHeaders.java b/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedHeaders.java
index 451e94f1e..dd228da0f 100644
--- a/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedHeaders.java
+++ b/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedHeaders.java
@@ -1,5 +1,7 @@
 package org.openapitools.openapidiff.core.model;
 
+import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.RESPONSE_HEADERS_DECREASED;
+
 import io.swagger.v3.oas.models.headers.Header;
 import java.util.ArrayList;
 import java.util.List;
@@ -31,10 +33,12 @@ public DiffResult isCoreChanged() {
     if (increased.isEmpty() && missing.isEmpty()) {
       return DiffResult.NO_CHANGES;
     }
-    if (missing.isEmpty()) {
-      return DiffResult.COMPATIBLE;
+    if (!missing.isEmpty()) {
+      if (RESPONSE_HEADERS_DECREASED.enabled(context)) {
+        return DiffResult.INCOMPATIBLE;
+      }
     }
-    return DiffResult.INCOMPATIBLE;
+    return DiffResult.COMPATIBLE;
   }
 
   public Map<String, Header> getOldHeaders() {
diff --git a/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedMediaType.java b/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedMediaType.java
index 7136dea04..43f9a443d 100644
--- a/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedMediaType.java
+++ b/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedMediaType.java
@@ -1,7 +1,7 @@
 package org.openapitools.openapidiff.core.model;
 
 import io.swagger.v3.oas.models.media.Schema;
-import java.util.Collections;
+import java.util.Arrays;
 import java.util.List;
 import java.util.Objects;
 
@@ -10,6 +10,8 @@ public class ChangedMediaType implements ComposedChanged {
   private final Schema newSchema;
   private final DiffContext context;
   private ChangedSchema schema;
+  private ChangedExamples examples;
+  private ChangedExample example;
 
   public ChangedMediaType(Schema oldSchema, Schema newSchema, DiffContext context) {
     this.oldSchema = oldSchema;
@@ -19,7 +21,7 @@ public ChangedMediaType(Schema oldSchema, Schema newSchema, DiffContext context)
 
   @Override
   public List<Changed> getChangedElements() {
-    return Collections.singletonList(schema);
+    return Arrays.asList(schema, examples, example);
   }
 
   @Override
@@ -43,11 +45,29 @@ public ChangedSchema getSchema() {
     return this.schema;
   }
 
+  public ChangedExamples getExamples() {
+    return this.examples;
+  }
+
+  public ChangedExample getExample() {
+    return this.example;
+  }
+
   public ChangedMediaType setSchema(final ChangedSchema schema) {
     this.schema = schema;
     return this;
   }
 
+  public ChangedMediaType setExamples(final ChangedExamples examples) {
+    this.examples = examples;
+    return this;
+  }
+
+  public ChangedMediaType setExample(final ChangedExample example) {
+    this.example = example;
+    return this;
+  }
+
   @Override
   public boolean equals(Object o) {
     if (this == o) return true;
@@ -56,12 +76,14 @@ public boolean equals(Object o) {
     return Objects.equals(oldSchema, that.oldSchema)
         && Objects.equals(newSchema, that.newSchema)
         && Objects.equals(context, that.context)
-        && Objects.equals(schema, that.schema);
+        && Objects.equals(schema, that.schema)
+        && Objects.equals(examples, that.examples)
+        && Objects.equals(example, that.example);
   }
 
   @Override
   public int hashCode() {
-    return Objects.hash(oldSchema, newSchema, context, schema);
+    return Objects.hash(oldSchema, newSchema, context, schema, examples, example);
   }
 
   @java.lang.Override
@@ -74,6 +96,10 @@ public java.lang.String toString() {
         + this.getContext()
         + ", schema="
         + this.getSchema()
+        + ", examples="
+        + this.getExamples()
+        + ", example="
+        + this.getExample()
         + ")";
   }
 }
diff --git a/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedOAuthFlow.java b/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedOAuthFlow.java
index 5409d1d99..b6179e9be 100644
--- a/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedOAuthFlow.java
+++ b/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedOAuthFlow.java
@@ -1,5 +1,9 @@
 package org.openapitools.openapidiff.core.model;
 
+import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.SECURITY_SCHEME_OAUTH2_AUTH_URL_CHANGED;
+import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.SECURITY_SCHEME_OAUTH2_REFRESH_URL_CHANGED;
+import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.SECURITY_SCHEME_OAUTH2_TOKEN_URL_CHANGED;
+
 import io.swagger.v3.oas.models.security.OAuthFlow;
 import java.util.Collections;
 import java.util.List;
@@ -8,14 +12,16 @@
 public class ChangedOAuthFlow implements ComposedChanged {
   private OAuthFlow oldOAuthFlow;
   private OAuthFlow newOAuthFlow;
+  private final DiffContext context;
   private boolean authorizationUrl;
   private boolean tokenUrl;
   private boolean refreshUrl;
   private ChangedExtensions extensions;
 
-  public ChangedOAuthFlow(OAuthFlow oldOAuthFlow, OAuthFlow newOAuthFlow) {
+  public ChangedOAuthFlow(OAuthFlow oldOAuthFlow, OAuthFlow newOAuthFlow, DiffContext context) {
     this.oldOAuthFlow = oldOAuthFlow;
     this.newOAuthFlow = newOAuthFlow;
+    this.context = context;
   }
 
   @Override
@@ -25,10 +31,25 @@ public List<Changed> getChangedElements() {
 
   @Override
   public DiffResult isCoreChanged() {
-    if (authorizationUrl || tokenUrl || refreshUrl) {
-      return DiffResult.INCOMPATIBLE;
+    if (!authorizationUrl && !tokenUrl && !refreshUrl) {
+      return DiffResult.NO_CHANGES;
+    }
+    if (authorizationUrl) {
+      if (SECURITY_SCHEME_OAUTH2_AUTH_URL_CHANGED.enabled(context)) {
+        return DiffResult.INCOMPATIBLE;
+      }
+    }
+    if (refreshUrl) {
+      if (SECURITY_SCHEME_OAUTH2_REFRESH_URL_CHANGED.enabled(context)) {
+        return DiffResult.INCOMPATIBLE;
+      }
+    }
+    if (tokenUrl) {
+      if (SECURITY_SCHEME_OAUTH2_TOKEN_URL_CHANGED.enabled(context)) {
+        return DiffResult.INCOMPATIBLE;
+      }
     }
-    return DiffResult.NO_CHANGES;
+    return DiffResult.COMPATIBLE;
   }
 
   public OAuthFlow getOldOAuthFlow() {
diff --git a/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedOpenApi.java b/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedOpenApi.java
index a1c796ac5..d0f23ca16 100644
--- a/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedOpenApi.java
+++ b/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedOpenApi.java
@@ -1,21 +1,30 @@
 package org.openapitools.openapidiff.core.model;
 
+import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.OPENAPI_ENDPOINTS_DECREASED;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
 import io.swagger.v3.oas.models.OpenAPI;
 import java.util.List;
 import java.util.Objects;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
+import org.openapitools.openapidiff.core.compare.OpenApiDiffOptions;
 import org.openapitools.openapidiff.core.utils.EndpointUtils;
 
 public class ChangedOpenApi implements ComposedChanged {
-  private OpenAPI oldSpecOpenApi;
-  private OpenAPI newSpecOpenApi;
+  private final OpenApiDiffOptions options;
+  @JsonIgnore private OpenAPI oldSpecOpenApi;
+  @JsonIgnore private OpenAPI newSpecOpenApi;
   private List<Endpoint> newEndpoints;
   private List<Endpoint> missingEndpoints;
   private List<ChangedOperation> changedOperations;
   private List<ChangedSchema> changedSchemas;
   private ChangedExtensions changedExtensions;
 
+  public ChangedOpenApi(OpenApiDiffOptions options) {
+    this.options = options;
+  }
+
   public List<Endpoint> getDeprecatedEndpoints() {
     return changedOperations.stream()
         .filter(ChangedOperation::isDeprecated)
@@ -39,10 +48,12 @@ public DiffResult isCoreChanged() {
     if (newEndpoints.isEmpty() && missingEndpoints.isEmpty()) {
       return DiffResult.NO_CHANGES;
     }
-    if (missingEndpoints.isEmpty()) {
-      return DiffResult.COMPATIBLE;
+    if (!missingEndpoints.isEmpty()) {
+      if (OPENAPI_ENDPOINTS_DECREASED.enabled(options.getConfig())) {
+        return DiffResult.INCOMPATIBLE;
+      }
     }
-    return DiffResult.INCOMPATIBLE;
+    return DiffResult.COMPATIBLE;
   }
 
   public OpenAPI getOldSpecOpenApi() {
diff --git a/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedOperation.java b/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedOperation.java
index 0da0516fc..2a6cc773a 100644
--- a/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedOperation.java
+++ b/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedOperation.java
@@ -64,6 +64,10 @@ public DiffResult resultRequestBody() {
     return requestBody == null ? DiffResult.NO_CHANGES : requestBody.isChanged();
   }
 
+  public DiffResult resultSecurityRequirements() {
+    return securityRequirements == null ? DiffResult.NO_CHANGES : securityRequirements.isChanged();
+  }
+
   public Operation getOldOperation() {
     return this.oldOperation;
   }
diff --git a/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedParameter.java b/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedParameter.java
index 998570186..4b8208e24 100644
--- a/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedParameter.java
+++ b/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedParameter.java
@@ -1,5 +1,7 @@
 package org.openapitools.openapidiff.core.model;
 
+import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.*;
+
 import io.swagger.v3.oas.models.parameters.Parameter;
 import java.util.Arrays;
 import java.util.List;
@@ -17,6 +19,8 @@ public class ChangedParameter implements ComposedChanged {
   private boolean changeExplode;
   private boolean changeAllowEmptyValue;
   private ChangedMetadata description;
+  private ChangedExamples examples;
+  private ChangedExample example;
   private ChangedSchema schema;
   private ChangedContent content;
   private ChangedExtensions extensions;
@@ -29,7 +33,7 @@ public ChangedParameter(String name, String in, DiffContext context) {
 
   @Override
   public List<Changed> getChangedElements() {
-    return Arrays.asList(description, schema, content, extensions);
+    return Arrays.asList(description, examples, example, schema, content, extensions);
   }
 
   @Override
@@ -41,13 +45,27 @@ public DiffResult isCoreChanged() {
         && !changeExplode) {
       return DiffResult.NO_CHANGES;
     }
-    if ((!changeRequired || Boolean.TRUE.equals(oldParameter.getRequired()))
-        && (!changeAllowEmptyValue || Boolean.TRUE.equals(newParameter.getAllowEmptyValue()))
-        && !changeStyle
-        && !changeExplode) {
-      return DiffResult.COMPATIBLE;
+    if (changeAllowEmptyValue && !Boolean.TRUE.equals(newParameter.getAllowEmptyValue())) {
+      if (REQUEST_PARAM_ALLOWEMPTY_DECREASED.enabled(context)) {
+        return DiffResult.INCOMPATIBLE;
+      }
+    }
+    if (changeExplode) {
+      if (REQUEST_PARAM_EXPLODE_CHANGED.enabled(context)) {
+        return DiffResult.INCOMPATIBLE;
+      }
     }
-    return DiffResult.INCOMPATIBLE;
+    if (changeRequired && !Boolean.TRUE.equals(oldParameter.getRequired())) {
+      if (REQUEST_PARAMS_REQUIRED_INCREASED.enabled(context)) {
+        return DiffResult.INCOMPATIBLE;
+      }
+    }
+    if (changeStyle) {
+      if (REQUEST_PARAM_STYLE_CHANGED.enabled(context)) {
+        return DiffResult.INCOMPATIBLE;
+      }
+    }
+    return DiffResult.COMPATIBLE;
   }
 
   public DiffContext getContext() {
@@ -94,6 +112,14 @@ public ChangedMetadata getDescription() {
     return this.description;
   }
 
+  public ChangedExamples getExamples() {
+    return this.examples;
+  }
+
+  public ChangedExample getExample() {
+    return this.example;
+  }
+
   public ChangedSchema getSchema() {
     return this.schema;
   }
@@ -156,6 +182,16 @@ public ChangedParameter setDescription(final ChangedMetadata description) {
     return this;
   }
 
+  public ChangedParameter setExamples(final ChangedExamples examples) {
+    this.examples = examples;
+    return this;
+  }
+
+  public ChangedParameter setExample(final ChangedExample example) {
+    this.example = example;
+    return this;
+  }
+
   public ChangedParameter setSchema(final ChangedSchema schema) {
     this.schema = schema;
     return this;
@@ -187,6 +223,8 @@ public boolean equals(Object o) {
         && Objects.equals(name, that.name)
         && Objects.equals(in, that.in)
         && Objects.equals(description, that.description)
+        && Objects.equals(examples, that.examples)
+        && Objects.equals(example, that.example)
         && Objects.equals(schema, that.schema)
         && Objects.equals(content, that.content)
         && Objects.equals(extensions, that.extensions);
@@ -206,6 +244,8 @@ public int hashCode() {
         changeExplode,
         changeAllowEmptyValue,
         description,
+        examples,
+        example,
         schema,
         content,
         extensions);
@@ -235,6 +275,10 @@ public java.lang.String toString() {
         + this.isChangeAllowEmptyValue()
         + ", description="
         + this.getDescription()
+        + ", examples="
+        + this.getExamples()
+        + ", example="
+        + this.getExample()
         + ", schema="
         + this.getSchema()
         + ", content="
diff --git a/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedParameters.java b/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedParameters.java
index 2b119bf80..86dc145fd 100644
--- a/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedParameters.java
+++ b/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedParameters.java
@@ -1,5 +1,8 @@
 package org.openapitools.openapidiff.core.model;
 
+import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.REQUEST_PARAMS_DECREASED;
+import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.REQUEST_PARAMS_REQUIRED_INCREASED;
+
 import io.swagger.v3.oas.models.parameters.Parameter;
 import java.util.ArrayList;
 import java.util.List;
@@ -33,11 +36,17 @@ public DiffResult isCoreChanged() {
     if (increased.isEmpty() && missing.isEmpty()) {
       return DiffResult.NO_CHANGES;
     }
-    if (increased.stream().noneMatch(p -> p.getRequired() != null && p.getRequired())
-        && missing.isEmpty()) {
-      return DiffResult.COMPATIBLE;
+    if (!missing.isEmpty()) {
+      if (REQUEST_PARAMS_DECREASED.enabled(context)) {
+        return DiffResult.INCOMPATIBLE;
+      }
+    }
+    if (increased.stream().anyMatch(p -> p.getRequired() != null && p.getRequired())) {
+      if (REQUEST_PARAMS_REQUIRED_INCREASED.enabled(context)) {
+        return DiffResult.INCOMPATIBLE;
+      }
     }
-    return DiffResult.INCOMPATIBLE;
+    return DiffResult.COMPATIBLE;
   }
 
   public List<Parameter> getOldParameterList() {
diff --git a/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedPath.java b/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedPath.java
index e9fa4093a..2ba9830e7 100644
--- a/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedPath.java
+++ b/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedPath.java
@@ -1,5 +1,7 @@
 package org.openapitools.openapidiff.core.model;
 
+import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.OPENAPI_ENDPOINTS_DECREASED;
+
 import io.swagger.v3.oas.models.Operation;
 import io.swagger.v3.oas.models.PathItem;
 import java.util.*;
@@ -36,10 +38,12 @@ public DiffResult isCoreChanged() {
     if (increased.isEmpty() && missing.isEmpty()) {
       return DiffResult.NO_CHANGES;
     }
-    if (missing.isEmpty()) {
-      return DiffResult.COMPATIBLE;
+    if (!missing.isEmpty()) {
+      if (OPENAPI_ENDPOINTS_DECREASED.enabled(context)) {
+        return DiffResult.INCOMPATIBLE;
+      }
     }
-    return DiffResult.INCOMPATIBLE;
+    return DiffResult.COMPATIBLE;
   }
 
   public String getPathUrl() {
diff --git a/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedPaths.java b/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedPaths.java
index 96af52785..5e032b498 100644
--- a/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedPaths.java
+++ b/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedPaths.java
@@ -1,16 +1,24 @@
 package org.openapitools.openapidiff.core.model;
 
+import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.OPENAPI_ENDPOINTS_DECREASED;
+
 import io.swagger.v3.oas.models.PathItem;
 import java.util.*;
+import org.openapitools.openapidiff.core.compare.OpenApiDiffOptions;
 
 public class ChangedPaths implements ComposedChanged {
+  private final OpenApiDiffOptions options;
   private final Map<String, PathItem> oldPathMap;
   private final Map<String, PathItem> newPathMap;
   private Map<String, PathItem> increased;
   private Map<String, PathItem> missing;
   private Map<String, ChangedPath> changed;
 
-  public ChangedPaths(Map<String, PathItem> oldPathMap, Map<String, PathItem> newPathMap) {
+  public ChangedPaths(
+      Map<String, PathItem> oldPathMap,
+      Map<String, PathItem> newPathMap,
+      OpenApiDiffOptions options) {
+    this.options = options;
     this.oldPathMap = oldPathMap;
     this.newPathMap = newPathMap;
     this.increased = new LinkedHashMap<>();
@@ -28,10 +36,12 @@ public DiffResult isCoreChanged() {
     if (increased.isEmpty() && missing.isEmpty()) {
       return DiffResult.NO_CHANGES;
     }
-    if (missing.isEmpty()) {
-      return DiffResult.COMPATIBLE;
+    if (!missing.isEmpty()) {
+      if (OPENAPI_ENDPOINTS_DECREASED.enabled(options.getConfig())) {
+        return DiffResult.INCOMPATIBLE;
+      }
     }
-    return DiffResult.INCOMPATIBLE;
+    return DiffResult.COMPATIBLE;
   }
 
   public Map<String, PathItem> getOldPathMap() {
diff --git a/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedRequestBody.java b/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedRequestBody.java
index 83fadd407..48cc4cf0e 100644
--- a/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedRequestBody.java
+++ b/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedRequestBody.java
@@ -1,5 +1,7 @@
 package org.openapitools.openapidiff.core.model;
 
+import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.OPENAPI_ENDPOINTS_DECREASED;
+
 import io.swagger.v3.oas.models.parameters.RequestBody;
 import java.util.Arrays;
 import java.util.List;
@@ -31,7 +33,10 @@ public DiffResult isCoreChanged() {
     if (!changeRequired) {
       return DiffResult.NO_CHANGES;
     }
-    return DiffResult.INCOMPATIBLE;
+    if (OPENAPI_ENDPOINTS_DECREASED.enabled(context)) {
+      return DiffResult.INCOMPATIBLE;
+    }
+    return DiffResult.COMPATIBLE;
   }
 
   public RequestBody getOldRequestBody() {
diff --git a/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedSchema.java b/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedSchema.java
index c6289f17d..3779ee853 100644
--- a/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedSchema.java
+++ b/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedSchema.java
@@ -1,14 +1,26 @@
 package org.openapitools.openapidiff.core.model;
 
-import io.swagger.v3.oas.models.PathItem;
+import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.RESPONSE_REQUIRED_DECREASED;
+import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.SCHEMA_DISCRIMINATOR_CHANGED;
+import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.SCHEMA_TYPE_CHANGED;
+
 import io.swagger.v3.oas.models.media.Schema;
 import java.util.*;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 import org.openapitools.openapidiff.core.model.schema.ChangedEnum;
+import org.openapitools.openapidiff.core.model.schema.ChangedMaxItems;
 import org.openapitools.openapidiff.core.model.schema.ChangedMaxLength;
+import org.openapitools.openapidiff.core.model.schema.ChangedMaxProperties;
+import org.openapitools.openapidiff.core.model.schema.ChangedMinItems;
+import org.openapitools.openapidiff.core.model.schema.ChangedMinProperties;
+import org.openapitools.openapidiff.core.model.schema.ChangedMultipleOf;
+import org.openapitools.openapidiff.core.model.schema.ChangedNullable;
+import org.openapitools.openapidiff.core.model.schema.ChangedNumericRange;
+import org.openapitools.openapidiff.core.model.schema.ChangedOneOfSchema;
 import org.openapitools.openapidiff.core.model.schema.ChangedReadOnly;
 import org.openapitools.openapidiff.core.model.schema.ChangedRequired;
+import org.openapitools.openapidiff.core.model.schema.ChangedUniqueItems;
 import org.openapitools.openapidiff.core.model.schema.ChangedWriteOnly;
 
 public class ChangedSchema implements ComposedChanged {
@@ -21,6 +33,8 @@ public class ChangedSchema implements ComposedChanged {
   protected Map<String, Schema<?>> missingProperties;
   protected boolean changeDeprecated;
   protected ChangedMetadata description;
+  protected ChangedExamples examples;
+  protected ChangedExample example;
   protected boolean changeTitle;
   protected ChangedRequired required;
   protected boolean changeDefault;
@@ -30,6 +44,14 @@ public class ChangedSchema implements ComposedChanged {
   protected ChangedWriteOnly writeOnly;
   protected boolean changedType;
   protected ChangedMaxLength maxLength;
+  protected ChangedNumericRange numericRange;
+  protected ChangedMultipleOf multipleOf;
+  protected ChangedMaxItems maxItems;
+  protected ChangedMinItems minItems;
+  protected ChangedMaxProperties maxProperties;
+  protected ChangedMinProperties minProperties;
+  protected ChangedNullable nullable;
+  protected ChangedUniqueItems uniqueItems;
   protected boolean discriminatorPropertyChanged;
   protected ChangedSchema items;
   protected ChangedOneOfSchema oneOfSchema;
@@ -101,6 +123,8 @@ public List<Changed> getChangedElements() {
                   changedProperties.values().stream(),
                   Stream.of(
                       description,
+                      examples,
+                      example,
                       readOnly,
                       writeOnly,
                       items,
@@ -109,6 +133,14 @@ public List<Changed> getChangedElements() {
                       enumeration,
                       required,
                       maxLength,
+                      numericRange,
+                      multipleOf,
+                      maxItems,
+                      minItems,
+                      maxProperties,
+                      minProperties,
+                      nullable,
+                      uniqueItems,
                       extensions))
               .collect(Collectors.toList());
     }
@@ -130,6 +162,7 @@ private DiffResult calculateCoreChanged() {
     if (!changedType
         && (oldSchema == null && newSchema == null || oldSchema != null && newSchema != null)
         && !changeFormat
+        && !changeDefault
         && increasedProperties.isEmpty()
         && missingProperties.isEmpty()
         && changedProperties.values().isEmpty()
@@ -137,22 +170,50 @@ private DiffResult calculateCoreChanged() {
         && !discriminatorPropertyChanged) {
       return DiffResult.NO_CHANGES;
     }
-    boolean compatibleForResponse =
-        missingProperties.isEmpty() && (oldSchema == null || newSchema != null);
-    if ((context.isRequest() && compatibleForRequest()
-            || context.isResponse() && compatibleForResponse)
-        && !changedType
-        && !discriminatorPropertyChanged) {
-      return DiffResult.COMPATIBLE;
+    if (changedType) {
+      if (SCHEMA_TYPE_CHANGED.enabled(context)) {
+        return DiffResult.INCOMPATIBLE;
+      }
+    }
+    if (discriminatorPropertyChanged) {
+      if (SCHEMA_DISCRIMINATOR_CHANGED.enabled(context)) {
+        return DiffResult.INCOMPATIBLE;
+      }
     }
-    return DiffResult.INCOMPATIBLE;
+
+    if (!compatibleForRequest() || !compatibleForResponse()) {
+      return DiffResult.INCOMPATIBLE;
+    }
+    return DiffResult.COMPATIBLE;
   }
 
   private boolean compatibleForRequest() {
-    if (PathItem.HttpMethod.PUT.equals(context.getMethod()) && !increasedProperties.isEmpty()) {
-      return false;
+    if (context.isRequest()) {
+      if (oldSchema == null && newSchema != null) {
+        // TODO: dead code? If not, create test.
+        return false;
+      }
+    }
+    return true;
+  }
+
+  private boolean compatibleForResponse() {
+    if (context.isResponse()) {
+      if (oldSchema != null) {
+        if (newSchema == null) {
+          // TODO: dead code? If not, create test.
+          return false;
+        }
+        if (oldSchema.getRequired() != null
+            && missingProperties.keySet().stream()
+                .anyMatch(prop -> oldSchema.getRequired().contains(prop))) {
+          if (RESPONSE_REQUIRED_DECREASED.enabled(context)) {
+            return false;
+          }
+        }
+      }
     }
-    return (oldSchema != null || newSchema == null);
+    return true;
   }
 
   public DiffContext getContext() {
@@ -187,6 +248,14 @@ public boolean isChangeDeprecated() {
     return this.changeDeprecated;
   }
 
+  public ChangedExamples getExamples() {
+    return this.examples;
+  }
+
+  public ChangedExample getExample() {
+    return this.example;
+  }
+
   public ChangedMetadata getDescription() {
     return this.description;
   }
@@ -227,6 +296,30 @@ public ChangedMaxLength getMaxLength() {
     return this.maxLength;
   }
 
+  public ChangedNumericRange getNumericRange() {
+    return this.numericRange;
+  }
+
+  public ChangedMultipleOf getMultipleOf() {
+    return this.multipleOf;
+  }
+
+  public ChangedMaxItems getMaxItems() {
+    return this.maxItems;
+  }
+
+  public ChangedMinItems getMinItems() {
+    return this.minItems;
+  }
+
+  public ChangedNullable getNullable() {
+    return this.nullable;
+  }
+
+  public ChangedUniqueItems getUniqueItems() {
+    return uniqueItems;
+  }
+
   public boolean isDiscriminatorPropertyChanged() {
     return this.discriminatorPropertyChanged;
   }
@@ -247,6 +340,14 @@ public ChangedExtensions getExtensions() {
     return this.extensions;
   }
 
+  public ChangedMaxProperties getMaxProperties() {
+    return this.maxProperties;
+  }
+
+  public ChangedMinProperties getMinProperties() {
+    return this.minProperties;
+  }
+
   public ChangedSchema setContext(final DiffContext context) {
     this.context = context;
     return this;
@@ -297,6 +398,18 @@ public ChangedSchema setDescription(final ChangedMetadata description) {
     return this;
   }
 
+  public ChangedSchema setExamples(final ChangedExamples examples) {
+    clearChangedCache();
+    this.examples = examples;
+    return this;
+  }
+
+  public ChangedSchema setExample(final ChangedExample example) {
+    clearChangedCache();
+    this.example = example;
+    return this;
+  }
+
   public ChangedSchema setChangeTitle(final boolean changeTitle) {
     clearChangedCache();
     this.changeTitle = changeTitle;
@@ -351,6 +464,42 @@ public ChangedSchema setMaxLength(final ChangedMaxLength maxLength) {
     return this;
   }
 
+  public ChangedSchema setNumericRange(final ChangedNumericRange numericRange) {
+    clearChangedCache();
+    this.numericRange = numericRange;
+    return this;
+  }
+
+  public ChangedSchema setMultipleOf(final ChangedMultipleOf multipleOf) {
+    clearChangedCache();
+    this.multipleOf = multipleOf;
+    return this;
+  }
+
+  public ChangedSchema setMaxItems(final ChangedMaxItems maxItems) {
+    clearChangedCache();
+    this.maxItems = maxItems;
+    return this;
+  }
+
+  public ChangedSchema setMinItems(final ChangedMinItems minItems) {
+    clearChangedCache();
+    this.minItems = minItems;
+    return this;
+  }
+
+  public ChangedSchema setNullable(final ChangedNullable nullable) {
+    clearChangedCache();
+    this.nullable = nullable;
+    return this;
+  }
+
+  public ChangedSchema setUniqueItems(ChangedUniqueItems uniqueItems) {
+    clearChangedCache();
+    this.uniqueItems = uniqueItems;
+    return this;
+  }
+
   public ChangedSchema setDiscriminatorPropertyChanged(final boolean discriminatorPropertyChanged) {
     clearChangedCache();
     this.discriminatorPropertyChanged = discriminatorPropertyChanged;
@@ -381,6 +530,18 @@ public ChangedSchema setExtensions(final ChangedExtensions extensions) {
     return this;
   }
 
+  public ChangedSchema setMaxProperties(final ChangedMaxProperties maxProperties) {
+    clearChangedCache();
+    this.maxProperties = maxProperties;
+    return this;
+  }
+
+  public ChangedSchema setMinProperties(final ChangedMinProperties minProperties) {
+    clearChangedCache();
+    this.minProperties = minProperties;
+    return this;
+  }
+
   @Override
   public boolean equals(Object o) {
     if (this == o) return true;
@@ -400,15 +561,25 @@ public boolean equals(Object o) {
         && Objects.equals(increasedProperties, that.increasedProperties)
         && Objects.equals(missingProperties, that.missingProperties)
         && Objects.equals(description, that.description)
+        && Objects.equals(examples, that.examples)
+        && Objects.equals(example, that.example)
         && Objects.equals(required, that.required)
         && Objects.equals(enumeration, that.enumeration)
         && Objects.equals(readOnly, that.readOnly)
         && Objects.equals(writeOnly, that.writeOnly)
         && Objects.equals(maxLength, that.maxLength)
+        && Objects.equals(numericRange, that.numericRange)
+        && Objects.equals(multipleOf, that.multipleOf)
+        && Objects.equals(maxItems, that.maxItems)
+        && Objects.equals(minItems, that.minItems)
+        && Objects.equals(nullable, that.nullable)
+        && Objects.equals(uniqueItems, that.uniqueItems)
         && Objects.equals(items, that.items)
         && Objects.equals(oneOfSchema, that.oneOfSchema)
         && Objects.equals(addProp, that.addProp)
-        && Objects.equals(extensions, that.extensions);
+        && Objects.equals(extensions, that.extensions)
+        && Objects.equals(maxProperties, that.maxProperties)
+        && Objects.equals(minProperties, that.minProperties);
   }
 
   @Override
@@ -423,6 +594,8 @@ public int hashCode() {
         missingProperties,
         changeDeprecated,
         description,
+        examples,
+        example,
         changeTitle,
         required,
         changeDefault,
@@ -432,11 +605,19 @@ public int hashCode() {
         writeOnly,
         changedType,
         maxLength,
+        numericRange,
+        multipleOf,
+        maxItems,
+        minItems,
+        nullable,
+        uniqueItems,
         discriminatorPropertyChanged,
         items,
         oneOfSchema,
         addProp,
-        extensions);
+        extensions,
+        maxProperties,
+        minProperties);
   }
 
   @java.lang.Override
@@ -459,6 +640,10 @@ public java.lang.String toString() {
         + this.isChangeDeprecated()
         + ", description="
         + this.getDescription()
+        + ", examples="
+        + this.getExamples()
+        + ", example="
+        + this.getExample()
         + ", changeTitle="
         + this.isChangeTitle()
         + ", required="
@@ -477,6 +662,18 @@ public java.lang.String toString() {
         + this.isChangedType()
         + ", maxLength="
         + this.getMaxLength()
+        + ", numericRange="
+        + this.getNumericRange()
+        + ", multipleOf="
+        + this.getMultipleOf()
+        + ", maxItems="
+        + this.getMaxItems()
+        + ", minItems="
+        + this.getMinItems()
+        + ", nullable="
+        + this.getNullable()
+        + ", uniqueItems="
+        + this.getUniqueItems()
         + ", discriminatorPropertyChanged="
         + this.isDiscriminatorPropertyChanged()
         + ", items="
@@ -487,6 +684,10 @@ public java.lang.String toString() {
         + this.getAddProp()
         + ", extensions="
         + this.getExtensions()
+        + ", maxProperties="
+        + this.getMaxProperties()
+        + ", minProperties="
+        + this.getMinProperties()
         + ")";
   }
 }
diff --git a/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedSecurityRequirement.java b/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedSecurityRequirement.java
index f8e61f7dc..0c5ed1535 100644
--- a/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedSecurityRequirement.java
+++ b/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedSecurityRequirement.java
@@ -1,5 +1,7 @@
 package org.openapitools.openapidiff.core.model;
 
+import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.SECURITY_REQUIREMENT_SCHEMES_INCREASED;
+
 import io.swagger.v3.oas.models.security.SecurityRequirement;
 import java.util.ArrayList;
 import java.util.List;
@@ -8,14 +10,18 @@
 public class ChangedSecurityRequirement implements ComposedChanged {
   private SecurityRequirement oldSecurityRequirement;
   private SecurityRequirement newSecurityRequirement;
+  private final DiffContext context;
   private SecurityRequirement missing;
   private SecurityRequirement increased;
   private List<ChangedSecurityScheme> changed;
 
   public ChangedSecurityRequirement(
-      SecurityRequirement oldSecurityRequirement, SecurityRequirement newSecurityRequirement) {
+      SecurityRequirement oldSecurityRequirement,
+      SecurityRequirement newSecurityRequirement,
+      DiffContext context) {
     this.oldSecurityRequirement = oldSecurityRequirement;
     this.newSecurityRequirement = newSecurityRequirement;
+    this.context = context;
     this.changed = new ArrayList<>();
   }
 
@@ -29,10 +35,12 @@ public DiffResult isCoreChanged() {
     if (increased == null && missing == null) {
       return DiffResult.NO_CHANGES;
     }
-    if (increased == null) {
-      return DiffResult.COMPATIBLE;
+    if (increased != null) {
+      if (SECURITY_REQUIREMENT_SCHEMES_INCREASED.enabled(context)) {
+        return DiffResult.INCOMPATIBLE;
+      }
     }
-    return DiffResult.INCOMPATIBLE;
+    return DiffResult.COMPATIBLE;
   }
 
   public void addMissing(String key, List<String> scopes) {
diff --git a/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedSecurityRequirements.java b/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedSecurityRequirements.java
index d8f813801..3a8f90a34 100644
--- a/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedSecurityRequirements.java
+++ b/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedSecurityRequirements.java
@@ -1,5 +1,7 @@
 package org.openapitools.openapidiff.core.model;
 
+import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.SECURITY_REQUIREMENTS_DECREASED;
+
 import io.swagger.v3.oas.models.security.SecurityRequirement;
 import java.util.ArrayList;
 import java.util.List;
@@ -9,15 +11,18 @@
 public class ChangedSecurityRequirements implements ComposedChanged {
   private List<SecurityRequirement> oldSecurityRequirements;
   private List<SecurityRequirement> newSecurityRequirements;
+  private final DiffContext context;
   private List<SecurityRequirement> missing;
   private List<SecurityRequirement> increased;
   private List<ChangedSecurityRequirement> changed;
 
   public ChangedSecurityRequirements(
       List<SecurityRequirement> oldSecurityRequirements,
-      List<SecurityRequirement> newSecurityRequirements) {
+      List<SecurityRequirement> newSecurityRequirements,
+      DiffContext context) {
     this.oldSecurityRequirements = oldSecurityRequirements;
     this.newSecurityRequirements = newSecurityRequirements;
+    this.context = context;
     this.changed = new ArrayList<>();
   }
 
@@ -31,10 +36,12 @@ public DiffResult isCoreChanged() {
     if (CollectionUtils.isEmpty(missing) && CollectionUtils.isEmpty(increased)) {
       return DiffResult.NO_CHANGES;
     }
-    if (CollectionUtils.isEmpty(missing)) {
-      return DiffResult.COMPATIBLE;
+    if (CollectionUtils.isNotEmpty(missing)) {
+      if (SECURITY_REQUIREMENTS_DECREASED.enabled(context)) {
+        return DiffResult.INCOMPATIBLE;
+      }
     }
-    return DiffResult.INCOMPATIBLE;
+    return DiffResult.COMPATIBLE;
   }
 
   public void addMissing(SecurityRequirement securityRequirement) {
diff --git a/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedSecurityScheme.java b/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedSecurityScheme.java
index f4a1682f6..1adb56b6f 100644
--- a/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedSecurityScheme.java
+++ b/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedSecurityScheme.java
@@ -1,5 +1,7 @@
 package org.openapitools.openapidiff.core.model;
 
+import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.*;
+
 import io.swagger.v3.oas.models.security.SecurityScheme;
 import java.util.Arrays;
 import java.util.List;
@@ -8,6 +10,7 @@
 public class ChangedSecurityScheme implements ComposedChanged {
   private SecurityScheme oldSecurityScheme;
   private SecurityScheme newSecurityScheme;
+  private final DiffContext context;
   private boolean changedType;
   private boolean changedIn;
   private boolean changedScheme;
@@ -18,9 +21,11 @@ public class ChangedSecurityScheme implements ComposedChanged {
   private ChangedOAuthFlows oAuthFlows;
   private ChangedExtensions extensions;
 
-  public ChangedSecurityScheme(SecurityScheme oldSecurityScheme, SecurityScheme newSecurityScheme) {
+  public ChangedSecurityScheme(
+      SecurityScheme oldSecurityScheme, SecurityScheme newSecurityScheme, DiffContext context) {
     this.oldSecurityScheme = oldSecurityScheme;
     this.newSecurityScheme = newSecurityScheme;
+    this.context = context;
   }
 
   @Override
@@ -38,15 +43,36 @@ public DiffResult isCoreChanged() {
         && (changedScopes == null || changedScopes.isUnchanged())) {
       return DiffResult.NO_CHANGES;
     }
-    if (!changedType
-        && !changedIn
-        && !changedScheme
-        && !changedBearerFormat
-        && !changedOpenIdConnectUrl
-        && (changedScopes == null || changedScopes.getIncreased().isEmpty())) {
-      return DiffResult.COMPATIBLE;
+
+    if (changedBearerFormat) {
+      if (SECURITY_SCHEME_BEARER_FORMAT_CHANGED.enabled(context)) {
+        return DiffResult.INCOMPATIBLE;
+      }
+    }
+    if (changedOpenIdConnectUrl) {
+      if (SECURITY_SCHEME_OPENIDCONNECT_URL_CHANGED.enabled(context)) {
+        return DiffResult.INCOMPATIBLE;
+      }
+    }
+    if (changedScheme) {
+      if (SECURITY_SCHEME_SCHEME_CHANGED.enabled(context)) {
+        return DiffResult.INCOMPATIBLE;
+      }
+    }
+    if (changedScopes != null && !changedScopes.getIncreased().isEmpty()) {
+      if (SECURITY_SCHEME_SCOPES_INCREASED.enabled(context)) {
+        return DiffResult.INCOMPATIBLE;
+      }
+    }
+    if (changedIn || changedType) {
+      // TODO: Dead code removal opportunity for changedType and changedIn. It appears that
+      // SecuritySchemaDiff will never be given the chance to detect differences TYPE and
+      // IN differences because that case has already been detected and filtered out by
+      // SecurityRequirementsDiff and recorded as a dropped requirement in
+      // ChangedSecurityRequirements.
+      return DiffResult.INCOMPATIBLE;
     }
-    return DiffResult.INCOMPATIBLE;
+    return DiffResult.COMPATIBLE;
   }
 
   public SecurityScheme getOldSecurityScheme() {
diff --git a/core/src/main/java/org/openapitools/openapidiff/core/model/DiffContext.java b/core/src/main/java/org/openapitools/openapidiff/core/model/DiffContext.java
index 7c8716ed4..f2003d375 100644
--- a/core/src/main/java/org/openapitools/openapidiff/core/model/DiffContext.java
+++ b/core/src/main/java/org/openapitools/openapidiff/core/model/DiffContext.java
@@ -1,21 +1,28 @@
 package org.openapitools.openapidiff.core.model;
 
+import com.fasterxml.jackson.annotation.JsonIgnore;
 import io.swagger.v3.oas.models.PathItem;
 import java.util.HashMap;
 import java.util.Map;
+import org.apache.commons.configuration2.Configuration;
 import org.apache.commons.lang3.builder.EqualsBuilder;
 import org.apache.commons.lang3.builder.HashCodeBuilder;
+import org.openapitools.openapidiff.core.compare.OpenApiDiffOptions;
 
 public class DiffContext {
 
+  @JsonIgnore private final OpenApiDiffOptions options;
   private String url;
   private Map<String, String> parameters;
   private PathItem.HttpMethod method;
   private boolean response;
   private boolean request;
   private Boolean required;
+  private String leftUrl;
+  private String rightUrl;
 
-  public DiffContext() {
+  public DiffContext(OpenApiDiffOptions options) {
+    this.options = options;
     parameters = new HashMap<>();
     response = false;
     request = true;
@@ -37,6 +44,20 @@ public DiffContext copyAsResponse() {
     return copy().setResponse();
   }
 
+  public DiffContext copyWithLeftRightUrls(String leftUrl, String rightUrl) {
+    return copy().setLeftAndRightUrls(leftUrl, rightUrl);
+  }
+
+  @JsonIgnore
+  public OpenApiDiffOptions getOptions() {
+    return options;
+  }
+
+  @JsonIgnore
+  public Configuration getConfig() {
+    return options.getConfig();
+  }
+
   private DiffContext setRequest() {
     this.request = true;
     this.response = false;
@@ -76,7 +97,7 @@ private DiffContext setMethod(PathItem.HttpMethod method) {
   }
 
   private DiffContext copy() {
-    DiffContext context = new DiffContext();
+    DiffContext context = new DiffContext(options);
     context.url = this.url;
     context.parameters = this.parameters;
     context.method = this.method;
@@ -104,6 +125,20 @@ private DiffContext setRequired(boolean required) {
     return this;
   }
 
+  public DiffContext setLeftAndRightUrls(String leftUrl, String rightUrl) {
+    this.leftUrl = leftUrl;
+    this.rightUrl = rightUrl;
+    return this;
+  }
+
+  public String getLeftUrl() {
+    return this.leftUrl;
+  }
+
+  public String getRightUrl() {
+    return this.rightUrl;
+  }
+
   @Override
   public boolean equals(Object o) {
     if (this == o) return true;
@@ -131,6 +166,8 @@ public int hashCode() {
         .append(response)
         .append(request)
         .append(required)
+        .append(leftUrl)
+        .append(rightUrl)
         .toHashCode();
   }
 }
diff --git a/core/src/main/java/org/openapitools/openapidiff/core/model/schema/ChangedEnum.java b/core/src/main/java/org/openapitools/openapidiff/core/model/schema/ChangedEnum.java
index 0dcd5e6e9..ef4486628 100644
--- a/core/src/main/java/org/openapitools/openapidiff/core/model/schema/ChangedEnum.java
+++ b/core/src/main/java/org/openapitools/openapidiff/core/model/schema/ChangedEnum.java
@@ -1,5 +1,8 @@
 package org.openapitools.openapidiff.core.model.schema;
 
+import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.REQUEST_ENUM_DECREASED;
+import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.RESPONSE_ENUM_INCREASED;
+
 import java.util.List;
 import org.openapitools.openapidiff.core.model.ChangedList;
 import org.openapitools.openapidiff.core.model.DiffContext;
@@ -13,10 +16,16 @@ public ChangedEnum(List<T> oldValue, List<T> newValue, DiffContext context) {
 
   @Override
   public DiffResult isItemsChanged() {
-    if (context.isRequest() && getMissing().isEmpty()
-        || context.isResponse() && getIncreased().isEmpty()) {
-      return DiffResult.COMPATIBLE;
+    if (context.isRequest() && !getMissing().isEmpty()) {
+      if (REQUEST_ENUM_DECREASED.enabled(context)) {
+        return DiffResult.INCOMPATIBLE;
+      }
+    }
+    if (context.isResponse() && !getIncreased().isEmpty()) {
+      if (RESPONSE_ENUM_INCREASED.enabled(context)) {
+        return DiffResult.INCOMPATIBLE;
+      }
     }
-    return DiffResult.INCOMPATIBLE;
+    return DiffResult.COMPATIBLE;
   }
 }
diff --git a/core/src/main/java/org/openapitools/openapidiff/core/model/schema/ChangedMaxItems.java b/core/src/main/java/org/openapitools/openapidiff/core/model/schema/ChangedMaxItems.java
new file mode 100644
index 000000000..1af1134b7
--- /dev/null
+++ b/core/src/main/java/org/openapitools/openapidiff/core/model/schema/ChangedMaxItems.java
@@ -0,0 +1,43 @@
+package org.openapitools.openapidiff.core.model.schema;
+
+import org.openapitools.openapidiff.core.model.Changed;
+import org.openapitools.openapidiff.core.model.DiffContext;
+import org.openapitools.openapidiff.core.model.DiffResult;
+
+public class ChangedMaxItems implements Changed {
+  private final Integer oldValue;
+  private final Integer newValue;
+  private final DiffContext context;
+
+  public ChangedMaxItems(Integer oldValue, Integer newValue, DiffContext context) {
+    this.oldValue = oldValue;
+    this.newValue = newValue;
+    this.context = context;
+  }
+
+  @Override
+  public DiffResult isChanged() {
+    if (oldValue == null && newValue == null) {
+      return DiffResult.NO_CHANGES;
+    }
+    if (oldValue == null || newValue == null) {
+      return DiffResult.COMPATIBLE;
+    }
+    if (newValue < oldValue) {
+      return DiffResult.INCOMPATIBLE;
+    }
+    return DiffResult.COMPATIBLE;
+  }
+
+  public Integer getOldValue() {
+    return oldValue;
+  }
+
+  public Integer getNewValue() {
+    return newValue;
+  }
+
+  public DiffContext getContext() {
+    return context;
+  }
+}
diff --git a/core/src/main/java/org/openapitools/openapidiff/core/model/schema/ChangedMaxLength.java b/core/src/main/java/org/openapitools/openapidiff/core/model/schema/ChangedMaxLength.java
index 8bb5933da..efb068bcf 100644
--- a/core/src/main/java/org/openapitools/openapidiff/core/model/schema/ChangedMaxLength.java
+++ b/core/src/main/java/org/openapitools/openapidiff/core/model/schema/ChangedMaxLength.java
@@ -1,5 +1,8 @@
 package org.openapitools.openapidiff.core.model.schema;
 
+import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.REQUEST_MAX_LENGTH_DECREASED;
+import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.RESPONSE_MAX_LENGTH_INCREASED;
+
 import java.util.Objects;
 import org.openapitools.openapidiff.core.model.Changed;
 import org.openapitools.openapidiff.core.model.DiffContext;
@@ -15,11 +18,17 @@ public DiffResult isChanged() {
     if (Objects.equals(oldValue, newValue)) {
       return DiffResult.NO_CHANGES;
     }
-    if (context.isRequest() && (newValue == null || oldValue != null && oldValue <= newValue)
-        || context.isResponse() && (oldValue == null || newValue != null && newValue <= oldValue)) {
-      return DiffResult.COMPATIBLE;
+    if (context.isRequest() && (oldValue == null || newValue != null && newValue < oldValue)) {
+      if (REQUEST_MAX_LENGTH_DECREASED.enabled(context)) {
+        return DiffResult.INCOMPATIBLE;
+      }
+    }
+    if (context.isResponse() && (newValue == null || oldValue != null && newValue > oldValue)) {
+      if (RESPONSE_MAX_LENGTH_INCREASED.enabled(context)) {
+        return DiffResult.INCOMPATIBLE;
+      }
     }
-    return DiffResult.INCOMPATIBLE;
+    return DiffResult.COMPATIBLE;
   }
 
   public ChangedMaxLength(
diff --git a/core/src/main/java/org/openapitools/openapidiff/core/model/schema/ChangedMaxProperties.java b/core/src/main/java/org/openapitools/openapidiff/core/model/schema/ChangedMaxProperties.java
new file mode 100644
index 000000000..c4c56af3f
--- /dev/null
+++ b/core/src/main/java/org/openapitools/openapidiff/core/model/schema/ChangedMaxProperties.java
@@ -0,0 +1,46 @@
+package org.openapitools.openapidiff.core.model.schema;
+
+import org.openapitools.openapidiff.core.model.Changed;
+import org.openapitools.openapidiff.core.model.DiffContext;
+import org.openapitools.openapidiff.core.model.DiffResult;
+
+public class ChangedMaxProperties implements Changed {
+  private Integer oldValue;
+  private Integer newValue;
+  private DiffContext context;
+
+  public ChangedMaxProperties(Integer oldValue, Integer newValue, DiffContext context) {
+    this.oldValue = oldValue;
+    this.newValue = newValue;
+    this.context = context;
+  }
+
+  public Integer getOldValue() {
+    return oldValue;
+  }
+
+  public Integer getNewValue() {
+    return newValue;
+  }
+
+  @Override
+  public DiffResult isChanged() {
+    if (oldValue == null && newValue == null) {
+      return DiffResult.NO_CHANGES;
+    }
+
+    if (oldValue == null) {
+      return DiffResult.INCOMPATIBLE;
+    }
+
+    if (newValue == null) {
+      return DiffResult.COMPATIBLE;
+    }
+
+    if (!oldValue.equals(newValue)) {
+      return oldValue > newValue ? DiffResult.INCOMPATIBLE : DiffResult.COMPATIBLE;
+    }
+
+    return DiffResult.NO_CHANGES;
+  }
+}
diff --git a/core/src/main/java/org/openapitools/openapidiff/core/model/schema/ChangedMinItems.java b/core/src/main/java/org/openapitools/openapidiff/core/model/schema/ChangedMinItems.java
new file mode 100644
index 000000000..7791893c0
--- /dev/null
+++ b/core/src/main/java/org/openapitools/openapidiff/core/model/schema/ChangedMinItems.java
@@ -0,0 +1,43 @@
+package org.openapitools.openapidiff.core.model.schema;
+
+import org.openapitools.openapidiff.core.model.Changed;
+import org.openapitools.openapidiff.core.model.DiffContext;
+import org.openapitools.openapidiff.core.model.DiffResult;
+
+public class ChangedMinItems implements Changed {
+  private final Integer oldValue;
+  private final Integer newValue;
+  private final DiffContext context;
+
+  public ChangedMinItems(Integer oldValue, Integer newValue, DiffContext context) {
+    this.oldValue = oldValue;
+    this.newValue = newValue;
+    this.context = context;
+  }
+
+  @Override
+  public DiffResult isChanged() {
+    if (oldValue == null && newValue == null) {
+      return DiffResult.NO_CHANGES;
+    }
+    if (oldValue == null || newValue == null) {
+      return DiffResult.COMPATIBLE;
+    }
+    if (newValue > oldValue) {
+      return DiffResult.INCOMPATIBLE;
+    }
+    return DiffResult.COMPATIBLE;
+  }
+
+  public Integer getOldValue() {
+    return oldValue;
+  }
+
+  public Integer getNewValue() {
+    return newValue;
+  }
+
+  public DiffContext getContext() {
+    return context;
+  }
+}
diff --git a/core/src/main/java/org/openapitools/openapidiff/core/model/schema/ChangedMinProperties.java b/core/src/main/java/org/openapitools/openapidiff/core/model/schema/ChangedMinProperties.java
new file mode 100644
index 000000000..f574b5c80
--- /dev/null
+++ b/core/src/main/java/org/openapitools/openapidiff/core/model/schema/ChangedMinProperties.java
@@ -0,0 +1,46 @@
+package org.openapitools.openapidiff.core.model.schema;
+
+import org.openapitools.openapidiff.core.model.Changed;
+import org.openapitools.openapidiff.core.model.DiffContext;
+import org.openapitools.openapidiff.core.model.DiffResult;
+
+public class ChangedMinProperties implements Changed {
+  private Integer oldValue;
+  private Integer newValue;
+  private DiffContext context;
+
+  public ChangedMinProperties(Integer oldValue, Integer newValue, DiffContext context) {
+    this.oldValue = oldValue;
+    this.newValue = newValue;
+    this.context = context;
+  }
+
+  public Integer getOldValue() {
+    return oldValue;
+  }
+
+  public Integer getNewValue() {
+    return newValue;
+  }
+
+  @Override
+  public DiffResult isChanged() {
+    if (oldValue == null && newValue == null) {
+      return DiffResult.NO_CHANGES;
+    }
+
+    if (oldValue == null) {
+      return DiffResult.INCOMPATIBLE;
+    }
+
+    if (newValue == null) {
+      return DiffResult.COMPATIBLE;
+    }
+
+    if (!oldValue.equals(newValue)) {
+      return newValue > oldValue ? DiffResult.INCOMPATIBLE : DiffResult.COMPATIBLE;
+    }
+
+    return DiffResult.NO_CHANGES;
+  }
+}
diff --git a/core/src/main/java/org/openapitools/openapidiff/core/model/schema/ChangedMultipleOf.java b/core/src/main/java/org/openapitools/openapidiff/core/model/schema/ChangedMultipleOf.java
new file mode 100644
index 000000000..adaf15710
--- /dev/null
+++ b/core/src/main/java/org/openapitools/openapidiff/core/model/schema/ChangedMultipleOf.java
@@ -0,0 +1,57 @@
+package org.openapitools.openapidiff.core.model.schema;
+
+import java.math.BigDecimal;
+import java.util.Objects;
+import org.openapitools.openapidiff.core.model.Changed;
+import org.openapitools.openapidiff.core.model.DiffResult;
+
+public class ChangedMultipleOf implements Changed {
+
+  private final BigDecimal left;
+  private final BigDecimal right;
+
+  public ChangedMultipleOf(BigDecimal leftMultipleOf, BigDecimal rightMultipleOf) {
+    this.left = leftMultipleOf;
+    this.right = rightMultipleOf;
+  }
+
+  @Override
+  public DiffResult isChanged() {
+    if (Objects.equals(left, right)) {
+      return DiffResult.NO_CHANGES;
+    }
+
+    // multipleof removed -> compatible
+    if (right == null) {
+      return DiffResult.COMPATIBLE;
+    }
+
+    return DiffResult.INCOMPATIBLE;
+  }
+
+  public BigDecimal getLeft() {
+    return left;
+  }
+
+  public BigDecimal getRight() {
+    return right;
+  }
+
+  @Override
+  public String toString() {
+    return "ChangedMultipleOf [left=" + left + ", right=" + right + "]";
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) return true;
+    if (o == null || getClass() != o.getClass()) return false;
+    ChangedMultipleOf that = (ChangedMultipleOf) o;
+    return Objects.equals(left, that.getLeft()) && Objects.equals(right, that.getRight());
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hash(left, right);
+  }
+}
diff --git a/core/src/main/java/org/openapitools/openapidiff/core/model/schema/ChangedNullable.java b/core/src/main/java/org/openapitools/openapidiff/core/model/schema/ChangedNullable.java
new file mode 100644
index 000000000..befc1d16d
--- /dev/null
+++ b/core/src/main/java/org/openapitools/openapidiff/core/model/schema/ChangedNullable.java
@@ -0,0 +1,54 @@
+package org.openapitools.openapidiff.core.model.schema;
+
+import java.util.Objects;
+import org.openapitools.openapidiff.core.model.Changed;
+import org.openapitools.openapidiff.core.model.DiffResult;
+
+public class ChangedNullable implements Changed {
+
+  private final Boolean left;
+  private final Boolean right;
+
+  public ChangedNullable(Boolean leftNullable, Boolean rightNullable) {
+    this.left = leftNullable;
+    this.right = rightNullable;
+  }
+
+  @Override
+  public DiffResult isChanged() {
+    boolean leftValue = left != null && left;
+    boolean rightValue = right != null && right;
+
+    if (leftValue != rightValue) {
+      return DiffResult.COMPATIBLE;
+    }
+
+    return DiffResult.NO_CHANGES;
+  }
+
+  public Boolean getLeft() {
+    return left;
+  }
+
+  public Boolean getRight() {
+    return right;
+  }
+
+  @Override
+  public String toString() {
+    return "ChangedNullable [left=" + left + ", right=" + right + "]";
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) return true;
+    if (o == null || getClass() != o.getClass()) return false;
+    ChangedNullable that = (ChangedNullable) o;
+    return Objects.equals(left, that.getLeft()) && Objects.equals(right, that.getRight());
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hash(left, right);
+  }
+}
diff --git a/core/src/main/java/org/openapitools/openapidiff/core/model/schema/ChangedNumericRange.java b/core/src/main/java/org/openapitools/openapidiff/core/model/schema/ChangedNumericRange.java
new file mode 100644
index 000000000..151c12fb6
--- /dev/null
+++ b/core/src/main/java/org/openapitools/openapidiff/core/model/schema/ChangedNumericRange.java
@@ -0,0 +1,182 @@
+package org.openapitools.openapidiff.core.model.schema;
+
+import static org.apache.commons.lang3.BooleanUtils.isTrue;
+import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.REQUEST_NUMERIC_RANGE_DECREASED;
+import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.RESPONSE_NUMERIC_RANGE_INCREASED;
+
+import java.math.BigDecimal;
+import java.util.Objects;
+import org.openapitools.openapidiff.core.model.Changed;
+import org.openapitools.openapidiff.core.model.DiffContext;
+import org.openapitools.openapidiff.core.model.DiffResult;
+
+public final class ChangedNumericRange implements Changed {
+  private final BigDecimal oldMinimumValue;
+  private final BigDecimal newMinimumValue;
+  private final BigDecimal oldMaximumValue;
+  private final BigDecimal newMaximumValue;
+  private final Boolean oldMinimumExclusiveValue;
+  private final Boolean newMinimumExclusiveValue;
+  private final Boolean oldMaximumExclusiveValue;
+  private final Boolean newMaximumExclusiveValue;
+  private final DiffContext context;
+
+  @Override
+  public DiffResult isChanged() {
+    if (Objects.equals(oldMinimumValue, newMinimumValue)
+        && Objects.equals(oldMaximumValue, newMaximumValue)
+        && Objects.equals(oldMinimumExclusiveValue, newMinimumExclusiveValue)
+        && Objects.equals(oldMaximumExclusiveValue, newMaximumExclusiveValue)) {
+      return DiffResult.NO_CHANGES;
+    }
+
+    if ((context.isRequest() && !REQUEST_NUMERIC_RANGE_DECREASED.enabled(context))
+        || (context.isResponse() && !RESPONSE_NUMERIC_RANGE_INCREASED.enabled(context))) {
+      return DiffResult.COMPATIBLE;
+    }
+
+    boolean exclusiveMaxOld = isTrue(oldMaximumExclusiveValue);
+    boolean exclusiveMinOld = isTrue(oldMinimumExclusiveValue);
+    boolean exclusiveMaxNew = isTrue(newMaximumExclusiveValue);
+    boolean exclusiveMinNew = isTrue(newMinimumExclusiveValue);
+    int diffMax = compare(oldMaximumValue, newMaximumValue, false);
+    int diffMin = compare(oldMinimumValue, newMinimumValue, true);
+
+    if (context.isRequest()) {
+      if (diffMax > 0 || (diffMax == 0 && !exclusiveMaxOld && exclusiveMaxNew)) {
+        return DiffResult.INCOMPATIBLE;
+      }
+      if (diffMin < 0 || (diffMin == 0 && !exclusiveMinOld && exclusiveMinNew)) {
+        return DiffResult.INCOMPATIBLE;
+      }
+    } else if (context.isResponse()) {
+      if (diffMax < 0 || (diffMax == 0 && exclusiveMaxOld && !exclusiveMaxNew)) {
+        return DiffResult.INCOMPATIBLE;
+      }
+      if (diffMin > 0 || (diffMin == 0 && exclusiveMinOld && !exclusiveMinNew)) {
+        return DiffResult.INCOMPATIBLE;
+      }
+    }
+    return DiffResult.COMPATIBLE;
+  }
+
+  private int compare(BigDecimal left, BigDecimal right, boolean nullMeansLessThan) {
+    if (left == null && right == null) {
+      return 0;
+    }
+    if (left == null) {
+      return nullMeansLessThan ? -1 : 1;
+    }
+    if (right == null) {
+      return nullMeansLessThan ? 1 : -1;
+    }
+    return left.unscaledValue().compareTo(right.unscaledValue());
+  }
+
+  public ChangedNumericRange(
+      final BigDecimal oldMinimumValue,
+      final BigDecimal newMinimumValue,
+      final BigDecimal oldMaximumValue,
+      final BigDecimal newMaximumValue,
+      final Boolean oldMinimumExclusiveValue,
+      final Boolean newMinimumExclusiveValue,
+      final Boolean oldMaximumExclusiveValue,
+      final Boolean newMaximumExclusiveValue,
+      final DiffContext context) {
+    this.oldMinimumValue = oldMinimumValue;
+    this.newMinimumValue = newMinimumValue;
+    this.oldMaximumValue = oldMaximumValue;
+    this.newMaximumValue = newMaximumValue;
+    this.oldMinimumExclusiveValue = oldMinimumExclusiveValue;
+    this.newMinimumExclusiveValue = newMinimumExclusiveValue;
+    this.oldMaximumExclusiveValue = oldMaximumExclusiveValue;
+    this.newMaximumExclusiveValue = newMaximumExclusiveValue;
+    this.context = context;
+  }
+
+  public BigDecimal getOldMinimumValue() {
+    return oldMinimumValue;
+  }
+
+  public BigDecimal getNewMinimumValue() {
+    return newMinimumValue;
+  }
+
+  public BigDecimal getOldMaximumValue() {
+    return oldMaximumValue;
+  }
+
+  public BigDecimal getNewMaximumValue() {
+    return newMaximumValue;
+  }
+
+  public Boolean getOldMinimumExclusiveValue() {
+    return oldMinimumExclusiveValue;
+  }
+
+  public Boolean getNewMinimumExclusiveValue() {
+    return newMinimumExclusiveValue;
+  }
+
+  public Boolean getOldMaximumExclusiveValue() {
+    return oldMaximumExclusiveValue;
+  }
+
+  public Boolean getNewMaximumExclusiveValue() {
+    return newMaximumExclusiveValue;
+  }
+
+  public DiffContext getContext() {
+    return this.context;
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) return true;
+    if (o == null || getClass() != o.getClass()) return false;
+    ChangedNumericRange that = (ChangedNumericRange) o;
+    return Objects.equals(oldMinimumValue, newMinimumValue)
+        && Objects.equals(oldMaximumValue, newMaximumValue)
+        && Objects.equals(oldMinimumExclusiveValue, newMinimumExclusiveValue)
+        && Objects.equals(oldMaximumExclusiveValue, newMaximumExclusiveValue)
+        && Objects.equals(context, that.context);
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hash(
+        oldMinimumValue,
+        newMinimumValue,
+        oldMaximumValue,
+        newMaximumValue,
+        oldMinimumExclusiveValue,
+        newMinimumExclusiveValue,
+        oldMaximumExclusiveValue,
+        newMaximumExclusiveValue,
+        context);
+  }
+
+  @Override
+  public String toString() {
+    return "ChangedNumericRange("
+        + "oldMinimumValue="
+        + oldMinimumValue
+        + ", newMinimumValue="
+        + newMinimumValue
+        + ", oldMaximumValue="
+        + oldMaximumValue
+        + ", newMaximumValue="
+        + newMaximumValue
+        + ", oldMinimumExclusiveValue="
+        + oldMinimumExclusiveValue
+        + ", newMinimumExclusiveValue="
+        + newMinimumExclusiveValue
+        + ", oldMaximumExclusiveValue="
+        + oldMaximumExclusiveValue
+        + ", newMaximumExclusiveValue="
+        + newMaximumExclusiveValue
+        + ", context="
+        + context
+        + ')';
+  }
+}
diff --git a/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedOneOfSchema.java b/core/src/main/java/org/openapitools/openapidiff/core/model/schema/ChangedOneOfSchema.java
similarity index 75%
rename from core/src/main/java/org/openapitools/openapidiff/core/model/ChangedOneOfSchema.java
rename to core/src/main/java/org/openapitools/openapidiff/core/model/schema/ChangedOneOfSchema.java
index ebf07d2b7..60159f569 100644
--- a/core/src/main/java/org/openapitools/openapidiff/core/model/ChangedOneOfSchema.java
+++ b/core/src/main/java/org/openapitools/openapidiff/core/model/schema/ChangedOneOfSchema.java
@@ -1,10 +1,18 @@
-package org.openapitools.openapidiff.core.model;
+package org.openapitools.openapidiff.core.model.schema;
+
+import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.REQUEST_ONEOF_DECREASED;
+import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.RESPONSE_ONEOF_INCREASED;
 
 import io.swagger.v3.oas.models.media.Schema;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
+import org.openapitools.openapidiff.core.model.Changed;
+import org.openapitools.openapidiff.core.model.ChangedSchema;
+import org.openapitools.openapidiff.core.model.ComposedChanged;
+import org.openapitools.openapidiff.core.model.DiffContext;
+import org.openapitools.openapidiff.core.model.DiffResult;
 
 public class ChangedOneOfSchema implements ComposedChanged {
   private final Map<String, String> oldMapping;
@@ -31,10 +39,17 @@ public DiffResult isCoreChanged() {
     if (increased.isEmpty() && missing.isEmpty()) {
       return DiffResult.NO_CHANGES;
     }
-    if (context.isRequest() && missing.isEmpty() || context.isResponse() && increased.isEmpty()) {
-      return DiffResult.COMPATIBLE;
+    if (context.isRequest() && !missing.isEmpty()) {
+      if (REQUEST_ONEOF_DECREASED.enabled(context)) {
+        return DiffResult.INCOMPATIBLE;
+      }
+    }
+    if (context.isResponse() && !increased.isEmpty()) {
+      if (RESPONSE_ONEOF_INCREASED.enabled(context)) {
+        return DiffResult.INCOMPATIBLE;
+      }
     }
-    return DiffResult.INCOMPATIBLE;
+    return DiffResult.COMPATIBLE;
   }
 
   public Map<String, String> getOldMapping() {
diff --git a/core/src/main/java/org/openapitools/openapidiff/core/model/schema/ChangedReadOnly.java b/core/src/main/java/org/openapitools/openapidiff/core/model/schema/ChangedReadOnly.java
index 25d86af8f..9bde49acd 100644
--- a/core/src/main/java/org/openapitools/openapidiff/core/model/schema/ChangedReadOnly.java
+++ b/core/src/main/java/org/openapitools/openapidiff/core/model/schema/ChangedReadOnly.java
@@ -1,5 +1,8 @@
 package org.openapitools.openapidiff.core.model.schema;
 
+import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.REQUEST_READONLY_INCREASED;
+import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.REQUEST_READONLY_REQUIRED_DECREASED;
+
 import java.util.Objects;
 import java.util.Optional;
 import org.openapitools.openapidiff.core.model.Changed;
@@ -29,10 +32,16 @@ public DiffResult isChanged() {
     }
     if (context.isRequest()) {
       if (Boolean.TRUE.equals(newValue)) {
-        return DiffResult.INCOMPATIBLE;
-      } else {
-        return context.isRequired() ? DiffResult.INCOMPATIBLE : DiffResult.COMPATIBLE;
+        if (REQUEST_READONLY_INCREASED.enabled(context)) {
+          return DiffResult.INCOMPATIBLE;
+        }
+      } else if (context.isRequired()) {
+        // Incompatible because a prev RO prop (invalid) is now not RO and required
+        if (REQUEST_READONLY_REQUIRED_DECREASED.enabled(context)) {
+          return DiffResult.INCOMPATIBLE;
+        }
       }
+      return DiffResult.COMPATIBLE;
     }
     return DiffResult.UNKNOWN;
   }
diff --git a/core/src/main/java/org/openapitools/openapidiff/core/model/schema/ChangedRequired.java b/core/src/main/java/org/openapitools/openapidiff/core/model/schema/ChangedRequired.java
index d6b41f3cd..58a9ae49a 100644
--- a/core/src/main/java/org/openapitools/openapidiff/core/model/schema/ChangedRequired.java
+++ b/core/src/main/java/org/openapitools/openapidiff/core/model/schema/ChangedRequired.java
@@ -1,5 +1,8 @@
 package org.openapitools.openapidiff.core.model.schema;
 
+import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.REQUEST_REQUIRED_INCREASED;
+import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.RESPONSE_REQUIRED_DECREASED;
+
 import java.util.List;
 import org.openapitools.openapidiff.core.model.ChangedList;
 import org.openapitools.openapidiff.core.model.DiffContext;
@@ -13,10 +16,16 @@ public ChangedRequired(List<String> oldValue, List<String> newValue, DiffContext
 
   @Override
   public DiffResult isItemsChanged() {
-    if (context.isRequest() && getIncreased().isEmpty()
-        || context.isResponse() && getMissing().isEmpty()) {
-      return DiffResult.COMPATIBLE;
+    if (context.isRequest() && !getIncreased().isEmpty()) {
+      if (REQUEST_REQUIRED_INCREASED.enabled(context)) {
+        return DiffResult.INCOMPATIBLE;
+      }
+    }
+    if (context.isResponse() && !getMissing().isEmpty()) {
+      if (RESPONSE_REQUIRED_DECREASED.enabled(context)) {
+        return DiffResult.INCOMPATIBLE;
+      }
     }
-    return DiffResult.INCOMPATIBLE;
+    return DiffResult.COMPATIBLE;
   }
 }
diff --git a/core/src/main/java/org/openapitools/openapidiff/core/model/schema/ChangedUniqueItems.java b/core/src/main/java/org/openapitools/openapidiff/core/model/schema/ChangedUniqueItems.java
new file mode 100644
index 000000000..ecccdf674
--- /dev/null
+++ b/core/src/main/java/org/openapitools/openapidiff/core/model/schema/ChangedUniqueItems.java
@@ -0,0 +1,58 @@
+package org.openapitools.openapidiff.core.model.schema;
+
+import java.util.Objects;
+import org.openapitools.openapidiff.core.model.Changed;
+import org.openapitools.openapidiff.core.model.DiffResult;
+
+public class ChangedUniqueItems implements Changed {
+
+  private final Boolean left;
+  private final Boolean right;
+
+  public ChangedUniqueItems(Boolean leftNullable, Boolean rightNullable) {
+    this.left = leftNullable;
+    this.right = rightNullable;
+  }
+
+  @Override
+  public DiffResult isChanged() {
+    boolean leftValue = left != null && left;
+    boolean rightValue = right != null && right;
+
+    if (leftValue == false && rightValue == true) {
+      return DiffResult.INCOMPATIBLE;
+    }
+
+    if (leftValue == true && rightValue == false) {
+      return DiffResult.COMPATIBLE;
+    }
+
+    return DiffResult.NO_CHANGES;
+  }
+
+  public Boolean getLeft() {
+    return left;
+  }
+
+  public Boolean getRight() {
+    return right;
+  }
+
+  @Override
+  public String toString() {
+    return "ChangedUniqueItems [left=" + left + ", right=" + right + "]";
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) return true;
+    if (o == null || getClass() != o.getClass()) return false;
+    ChangedNullable that = (ChangedNullable) o;
+    return Objects.equals(left, that.getLeft()) && Objects.equals(right, that.getRight());
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hash(left, right);
+  }
+}
diff --git a/core/src/main/java/org/openapitools/openapidiff/core/model/schema/ChangedWriteOnly.java b/core/src/main/java/org/openapitools/openapidiff/core/model/schema/ChangedWriteOnly.java
index 91d19a644..110b6ede2 100644
--- a/core/src/main/java/org/openapitools/openapidiff/core/model/schema/ChangedWriteOnly.java
+++ b/core/src/main/java/org/openapitools/openapidiff/core/model/schema/ChangedWriteOnly.java
@@ -1,5 +1,8 @@
 package org.openapitools.openapidiff.core.model.schema;
 
+import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.RESPONSE_WRITEONLY_INCREASED;
+import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.RESPONSE_WRITEONLY_REQUIRED_DECREASED;
+
 import java.util.Objects;
 import java.util.Optional;
 import org.openapitools.openapidiff.core.model.Changed;
@@ -29,10 +32,15 @@ public DiffResult isChanged() {
     }
     if (context.isResponse()) {
       if (Boolean.TRUE.equals(newValue)) {
-        return DiffResult.INCOMPATIBLE;
-      } else {
-        return context.isRequired() ? DiffResult.INCOMPATIBLE : DiffResult.COMPATIBLE;
+        if (RESPONSE_WRITEONLY_INCREASED.enabled(context)) {
+          return DiffResult.INCOMPATIBLE;
+        }
+      } else if (context.isRequired()) {
+        if (RESPONSE_WRITEONLY_REQUIRED_DECREASED.enabled(context)) {
+          return DiffResult.INCOMPATIBLE;
+        }
       }
+      return DiffResult.COMPATIBLE;
     }
     return DiffResult.UNKNOWN;
   }
diff --git a/core/src/main/java/org/openapitools/openapidiff/core/output/AsciidocRender.java b/core/src/main/java/org/openapitools/openapidiff/core/output/AsciidocRender.java
new file mode 100644
index 000000000..997241dc2
--- /dev/null
+++ b/core/src/main/java/org/openapitools/openapidiff/core/output/AsciidocRender.java
@@ -0,0 +1,311 @@
+package org.openapitools.openapidiff.core.output;
+
+import static org.openapitools.openapidiff.core.model.Changed.result;
+
+import io.swagger.v3.oas.models.media.ArraySchema;
+import io.swagger.v3.oas.models.media.Schema;
+import io.swagger.v3.oas.models.parameters.Parameter;
+import io.swagger.v3.oas.models.responses.ApiResponse;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Optional;
+import org.apache.commons.lang3.StringUtils;
+import org.openapitools.openapidiff.core.exception.RendererException;
+import org.openapitools.openapidiff.core.model.*;
+import org.openapitools.openapidiff.core.utils.RefPointer;
+import org.openapitools.openapidiff.core.utils.RefType;
+
+public class AsciidocRender implements Render {
+  private static final int LINE_LENGTH = 74;
+  protected static RefPointer<Schema<?>> refPointer = new RefPointer<>(RefType.SCHEMAS);
+  protected ChangedOpenApi diff;
+
+  @Override
+  public void render(ChangedOpenApi diff, OutputStreamWriter outputStreamWriter) {
+    this.diff = diff;
+    if (diff.isUnchanged()) {
+      safelyAppend(
+          outputStreamWriter,
+          bigTitle(
+              diff.getNewSpecOpenApi().getInfo().getTitle(),
+              diff.getNewSpecOpenApi().getInfo().getVersion()));
+      safelyAppend(outputStreamWriter, System.lineSeparator());
+      safelyAppend(outputStreamWriter, System.lineSeparator());
+      safelyAppend(outputStreamWriter, "NOTE: No differences. Specifications are equivalents");
+    } else {
+      safelyAppend(
+          outputStreamWriter,
+          bigTitle(
+              diff.getNewSpecOpenApi().getInfo().getTitle(),
+              diff.getNewSpecOpenApi().getInfo().getVersion()));
+      safelyAppend(outputStreamWriter, System.lineSeparator());
+      safelyAppend(outputStreamWriter, ":reproducible:\n:sectlinks:\n:toc:\n");
+      safelyAppend(outputStreamWriter, System.lineSeparator());
+
+      List<Endpoint> newEndpoints = diff.getNewEndpoints();
+      listEndpoints(newEndpoints, "What's New", outputStreamWriter);
+
+      List<Endpoint> missingEndpoints = diff.getMissingEndpoints();
+      listEndpoints(missingEndpoints, "What's Deleted", outputStreamWriter);
+
+      List<Endpoint> deprecatedEndpoints = diff.getDeprecatedEndpoints();
+      listEndpoints(deprecatedEndpoints, "What's Deprecated", outputStreamWriter);
+
+      List<ChangedOperation> changedOperations = diff.getChangedOperations();
+      ol_changed(changedOperations, outputStreamWriter);
+
+      safelyAppend(outputStreamWriter, System.lineSeparator());
+      safelyAppend(
+          outputStreamWriter,
+          diff.isCompatible()
+              ? "NOTE: API changes are backward compatible"
+              : "WARNING: API changes broke backward compatibility");
+      safelyAppend(outputStreamWriter, System.lineSeparator());
+    }
+    try {
+      outputStreamWriter.close();
+    } catch (IOException e) {
+      throw new RendererException(e);
+    }
+  }
+
+  private void ol_changed(
+      List<ChangedOperation> operations, OutputStreamWriter outputStreamWriter) {
+    if (null == operations || operations.isEmpty()) {
+      return;
+    }
+    safelyAppend(outputStreamWriter, title("What's Changed", 2));
+    safelyAppend(outputStreamWriter, System.lineSeparator());
+    for (ChangedOperation operation : operations) {
+      String pathUrl = operation.getPathUrl();
+      String method = operation.getHttpMethod().toString();
+      String desc =
+          Optional.ofNullable(operation.getSummary()).map(ChangedMetadata::getRight).orElse("");
+
+      safelyAppend(outputStreamWriter, itemEndpoint(method, pathUrl, desc));
+      if (result(operation.getParameters()).isDifferent()) {
+        safelyAppend(outputStreamWriter, "* Parameter:\n");
+        safelyAppend(outputStreamWriter, ul_param(operation.getParameters()));
+        safelyAppend(outputStreamWriter, System.lineSeparator());
+      }
+      if (operation.resultRequestBody().isDifferent()) {
+        safelyAppend(outputStreamWriter, "* Request:\n");
+        safelyAppend(
+            outputStreamWriter, ul_content(operation.getRequestBody().getContent(), true, 2));
+        safelyAppend(outputStreamWriter, System.lineSeparator());
+      }
+      if (operation.resultApiResponses().isDifferent()) {
+        safelyAppend(outputStreamWriter, "* Return Type:\n");
+        safelyAppend(outputStreamWriter, ul_response(operation.getApiResponses()));
+        safelyAppend(outputStreamWriter, System.lineSeparator());
+      }
+    }
+  }
+
+  private String ul_response(ChangedApiResponse changedApiResponse) {
+    Map<String, ApiResponse> addResponses = changedApiResponse.getIncreased();
+    Map<String, ApiResponse> delResponses = changedApiResponse.getMissing();
+    Map<String, ChangedResponse> changedResponses = changedApiResponse.getChanged();
+    StringBuilder sb = new StringBuilder();
+    for (String propName : addResponses.keySet()) {
+      sb.append(itemResponse("** Add ", propName));
+    }
+    for (String propName : delResponses.keySet()) {
+      sb.append(itemResponse("** Deleted ", propName));
+    }
+    for (Entry<String, ChangedResponse> entry : changedResponses.entrySet()) {
+      sb.append(itemChangedResponse("** Changed ", entry.getKey(), entry.getValue()));
+    }
+    return sb.toString();
+  }
+
+  private String itemResponse(String title, String code) {
+    StringBuilder sb = new StringBuilder();
+    String status = "";
+    if (!code.equals("default") && !code.matches("[1-5]XX")) {
+      status = HttpStatus.getReasonPhrase(Integer.parseInt(code));
+    }
+    sb.append(title).append(code).append(' ').append(status).append("\n");
+    return sb.toString();
+  }
+
+  private String itemChangedResponse(String title, String contentType, ChangedResponse response) {
+    return itemResponse(title, contentType)
+        + "** Media types:\n"
+        + ul_content(response.getContent(), false, 3);
+  }
+
+  private String ul_content(ChangedContent changedContent, boolean isRequest, int indent) {
+    StringBuilder sb = new StringBuilder();
+    if (changedContent == null) {
+      return sb.toString();
+    }
+    for (String propName : changedContent.getIncreased().keySet()) {
+      sb.append(itemContent("Added ", propName, indent));
+    }
+    for (String propName : changedContent.getMissing().keySet()) {
+      sb.append(itemContent("Deleted ", propName, indent));
+    }
+    for (String propName : changedContent.getChanged().keySet()) {
+      sb.append(
+          itemContent(
+              "Changed ", propName, indent, changedContent.getChanged().get(propName), isRequest));
+    }
+    return sb.toString();
+  }
+
+  private String itemContent(String title, String contentType, int indent) {
+    return StringUtils.repeat('*', indent) + " " + title + contentType + "\n";
+  }
+
+  private String itemContent(
+      String title,
+      String contentType,
+      int indent,
+      ChangedMediaType changedMediaType,
+      boolean isRequest) {
+    StringBuilder sb = new StringBuilder();
+    sb.append(itemContent(title, contentType, indent))
+        .append(itemContent("Schema:", "", indent))
+        .append(changedMediaType.isCompatible() ? "Backward compatible" : "Broken compatibility")
+        .append("\n");
+    if (!changedMediaType.isCompatible()) {
+      sb.append(incompatibilities(changedMediaType.getSchema()));
+    }
+    return sb.toString();
+  }
+
+  private String incompatibilities(final ChangedSchema schema) {
+    return incompatibilities("", schema);
+  }
+
+  private String incompatibilities(String propName, final ChangedSchema schema) {
+    StringBuilder sb = new StringBuilder();
+    if (schema.getItems() != null) {
+      sb.append(items(propName, schema.getItems()));
+    }
+    if (schema.isCoreChanged() == DiffResult.INCOMPATIBLE && schema.isChangedType()) {
+      String type = type(schema.getOldSchema()) + " -> " + type(schema.getNewSchema());
+      sb.append(property(propName, "Changed property type", type));
+    }
+    String prefix = propName.isEmpty() ? "" : propName + ".";
+    sb.append(
+        properties(prefix, "Missing property", schema.getMissingProperties(), schema.getContext()));
+    schema
+        .getChangedProperties()
+        .forEach((name, property) -> sb.append(incompatibilities(prefix + name, property)));
+    return sb.toString();
+  }
+
+  private String items(String propName, ChangedSchema schema) {
+    return incompatibilities(propName + "[n]", schema);
+  }
+
+  private String properties(
+      String propPrefix, String title, Map<String, Schema<?>> properties, DiffContext context) {
+    StringBuilder sb = new StringBuilder();
+    if (properties != null) {
+      properties.forEach((key, value) -> sb.append(resolveProperty(propPrefix, value, key, title)));
+    }
+    return sb.toString();
+  }
+
+  private String resolveProperty(String propPrefix, Schema<?> value, String key, String title) {
+    try {
+      return property(propPrefix + key, title, resolve(value));
+    } catch (Exception e) {
+      return property(propPrefix + key, title, type(value));
+    }
+  }
+
+  protected String property(String name, String title, Schema<?> schema) {
+    return property(name, title, type(schema));
+  }
+
+  protected String property(String name, String title, String type) {
+    return String.format("*** %s: %s (%s)%n\n", title, name, type);
+  }
+
+  protected Schema<?> resolve(Schema<?> schema) {
+    return refPointer.resolveRef(
+        diff.getNewSpecOpenApi().getComponents(), schema, schema.get$ref());
+  }
+
+  protected String type(Schema<?> schema) {
+    String result = "object";
+    if (schema == null) {
+      result = "no schema";
+    } else if (schema instanceof ArraySchema) {
+      result = "array";
+    } else if (schema.getType() != null) {
+      result = schema.getType();
+    }
+    return result;
+  }
+
+  private String ul_param(ChangedParameters changedParameters) {
+    List<Parameter> addParameters = changedParameters.getIncreased();
+    List<Parameter> delParameters = changedParameters.getMissing();
+    List<ChangedParameter> changed = changedParameters.getChanged();
+    StringBuilder sb = new StringBuilder();
+    for (Parameter param : addParameters) {
+      sb.append(itemParam("** Add ", param));
+    }
+    for (ChangedParameter param : changed) {
+      sb.append(li_changedParam(param));
+    }
+    for (Parameter param : delParameters) {
+      sb.append(itemParam("** Delete ", param));
+    }
+    return sb.toString();
+  }
+
+  private String itemParam(String title, Parameter param) {
+    return title + param.getName() + " in " + param.getIn() + System.lineSeparator();
+  }
+
+  private String li_changedParam(ChangedParameter changeParam) {
+    if (changeParam.isDeprecated()) {
+      return itemParam("** Deprecated ", changeParam.getNewParameter());
+    } else {
+      return itemParam("** Changed ", changeParam.getNewParameter());
+    }
+  }
+
+  private String listEndpoints(
+      List<Endpoint> endpoints, String title, OutputStreamWriter outputStreamWriter) {
+    if (null == endpoints || endpoints.isEmpty()) {
+      return "";
+    }
+    StringBuilder sb = new StringBuilder();
+    sb.append(title(title));
+    for (Endpoint endpoint : endpoints) {
+      sb.append(
+          itemEndpoint(
+              endpoint.getMethod().toString(), endpoint.getPathUrl(), endpoint.getSummary()));
+    }
+    return sb.append(System.lineSeparator()).toString();
+  }
+
+  private String itemEndpoint(String method, String path, String desc) {
+    return String.format("=== %s%s%n", StringUtils.rightPad(method, 6), path);
+  }
+
+  public String bigTitle(String title, String version) {
+    char ch = '=';
+
+    return String.format("= %s (v %s)", title.toUpperCase(), version);
+  }
+
+  public String title(String title) {
+    return this.title(title, '-');
+  }
+
+  public String title(String title, int level) {
+    String little = StringUtils.repeat("=", level);
+    return String.format("%s %s", little, title);
+  }
+}
diff --git a/core/src/main/java/org/openapitools/openapidiff/core/output/ConsoleRender.java b/core/src/main/java/org/openapitools/openapidiff/core/output/ConsoleRender.java
index 7ca4acc44..4f8e10fc9 100644
--- a/core/src/main/java/org/openapitools/openapidiff/core/output/ConsoleRender.java
+++ b/core/src/main/java/org/openapitools/openapidiff/core/output/ConsoleRender.java
@@ -6,12 +6,14 @@
 import io.swagger.v3.oas.models.media.Schema;
 import io.swagger.v3.oas.models.parameters.Parameter;
 import io.swagger.v3.oas.models.responses.ApiResponse;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
 import java.util.Optional;
-import org.apache.commons.httpclient.HttpStatus;
 import org.apache.commons.lang3.StringUtils;
+import org.openapitools.openapidiff.core.exception.RendererException;
 import org.openapitools.openapidiff.core.model.*;
 import org.openapitools.openapidiff.core.utils.RefPointer;
 import org.openapitools.openapidiff.core.utils.RefType;
@@ -22,81 +24,80 @@ public class ConsoleRender implements Render {
   protected ChangedOpenApi diff;
 
   @Override
-  public String render(ChangedOpenApi diff) {
+  public void render(ChangedOpenApi diff, OutputStreamWriter outputStreamWriter) {
     this.diff = diff;
-    StringBuilder output = new StringBuilder();
     if (diff.isUnchanged()) {
-      output.append("No differences. Specifications are equivalents");
+      safelyAppend(outputStreamWriter, "No differences. Specifications are equivalents");
     } else {
-      output
-          .append(bigTitle("Api Change Log"))
-          .append(StringUtils.center(diff.getNewSpecOpenApi().getInfo().getTitle(), LINE_LENGTH))
-          .append(System.lineSeparator());
+      safelyAppend(outputStreamWriter, bigTitle("Api Change Log"));
+      safelyAppend(
+          outputStreamWriter,
+          StringUtils.center(diff.getNewSpecOpenApi().getInfo().getTitle(), LINE_LENGTH));
+      safelyAppend(outputStreamWriter, System.lineSeparator());
 
       List<Endpoint> newEndpoints = diff.getNewEndpoints();
-      String ol_newEndpoint = listEndpoints(newEndpoints, "What's New");
+      listEndpoints(newEndpoints, "What's New", outputStreamWriter);
 
       List<Endpoint> missingEndpoints = diff.getMissingEndpoints();
-      String ol_missingEndpoint = listEndpoints(missingEndpoints, "What's Deleted");
+      listEndpoints(missingEndpoints, "What's Deleted", outputStreamWriter);
 
       List<Endpoint> deprecatedEndpoints = diff.getDeprecatedEndpoints();
-      String ol_deprecatedEndpoint = listEndpoints(deprecatedEndpoints, "What's Deprecated");
+      listEndpoints(deprecatedEndpoints, "What's Deprecated", outputStreamWriter);
 
       List<ChangedOperation> changedOperations = diff.getChangedOperations();
-      String ol_changed = ol_changed(changedOperations);
-
-      output
-          .append(renderBody(ol_newEndpoint, ol_missingEndpoint, ol_deprecatedEndpoint, ol_changed))
-          .append(title("Result"))
-          .append(
-              StringUtils.center(
-                  diff.isCompatible()
-                      ? "API changes are backward compatible"
-                      : "API changes broke backward compatibility",
-                  LINE_LENGTH))
-          .append(System.lineSeparator())
-          .append(separator('-'));
+      ol_changed(changedOperations, outputStreamWriter);
+
+      safelyAppend(outputStreamWriter, title("Result"));
+      safelyAppend(
+          outputStreamWriter,
+          StringUtils.center(
+              diff.isCompatible()
+                  ? "API changes are backward compatible"
+                  : "API changes broke backward compatibility",
+              LINE_LENGTH));
+      safelyAppend(outputStreamWriter, System.lineSeparator());
+      safelyAppend(outputStreamWriter, separator('-'));
+    }
+    try {
+      outputStreamWriter.close();
+    } catch (IOException e) {
+      throw new RendererException(e);
     }
-    return output.toString();
   }
 
-  private String ol_changed(List<ChangedOperation> operations) {
+  private void ol_changed(
+      List<ChangedOperation> operations, OutputStreamWriter outputStreamWriter) {
     if (null == operations || operations.isEmpty()) {
-      return "";
+      return;
     }
-    StringBuilder sb = new StringBuilder();
-    sb.append(title("What's Changed"));
+    safelyAppend(outputStreamWriter, title("What's Changed"));
     for (ChangedOperation operation : operations) {
       String pathUrl = operation.getPathUrl();
       String method = operation.getHttpMethod().toString();
       String desc =
           Optional.ofNullable(operation.getSummary()).map(ChangedMetadata::getRight).orElse("");
 
-      StringBuilder ul_detail = new StringBuilder();
+      safelyAppend(outputStreamWriter, itemEndpoint(method, pathUrl, desc));
+
       if (result(operation.getParameters()).isDifferent()) {
-        ul_detail
-            .append(StringUtils.repeat(' ', 2))
-            .append("Parameter:")
-            .append(System.lineSeparator())
-            .append(ul_param(operation.getParameters()));
+        safelyAppend(outputStreamWriter, StringUtils.repeat(' ', 2));
+        safelyAppend(outputStreamWriter, "Parameter:");
+        safelyAppend(outputStreamWriter, System.lineSeparator());
+        safelyAppend(outputStreamWriter, ul_param(operation.getParameters()));
       }
       if (operation.resultRequestBody().isDifferent()) {
-        ul_detail
-            .append(StringUtils.repeat(' ', 2))
-            .append("Request:")
-            .append(System.lineSeparator())
-            .append(ul_content(operation.getRequestBody().getContent(), true));
+        safelyAppend(outputStreamWriter, StringUtils.repeat(' ', 2));
+        safelyAppend(outputStreamWriter, "Request:");
+        safelyAppend(outputStreamWriter, System.lineSeparator());
+        safelyAppend(outputStreamWriter, ul_content(operation.getRequestBody().getContent(), true));
       }
       if (operation.resultApiResponses().isDifferent()) {
-        ul_detail
-            .append(StringUtils.repeat(' ', 2))
-            .append("Return Type:")
-            .append(System.lineSeparator())
-            .append(ul_response(operation.getApiResponses()));
+        safelyAppend(outputStreamWriter, StringUtils.repeat(' ', 2));
+        safelyAppend(outputStreamWriter, "Return Type:");
+        safelyAppend(outputStreamWriter, System.lineSeparator());
+        safelyAppend(outputStreamWriter, ul_response(operation.getApiResponses()));
       }
-      sb.append(itemEndpoint(method, pathUrl, desc)).append(ul_detail);
     }
-    return sb.toString();
   }
 
   private String ul_response(ChangedApiResponse changedApiResponse) {
@@ -119,8 +120,8 @@ private String ul_response(ChangedApiResponse changedApiResponse) {
   private String itemResponse(String title, String code) {
     StringBuilder sb = new StringBuilder();
     String status = "";
-    if (!code.equals("default")) {
-      status = HttpStatus.getStatusText(Integer.parseInt(code));
+    if (!code.equals("default") && !code.matches("[1-5]XX")) {
+      status = HttpStatus.getReasonPhrase(Integer.parseInt(code));
     }
     sb.append(StringUtils.repeat(' ', 4))
         .append("- ")
@@ -280,9 +281,10 @@ private String li_changedParam(ChangedParameter changeParam) {
     }
   }
 
-  private String listEndpoints(List<Endpoint> endpoints, String title) {
+  private void listEndpoints(
+      List<Endpoint> endpoints, String title, OutputStreamWriter outputStreamWriter) {
     if (null == endpoints || endpoints.isEmpty()) {
-      return "";
+      return;
     }
     StringBuilder sb = new StringBuilder();
     sb.append(title(title));
@@ -291,7 +293,8 @@ private String listEndpoints(List<Endpoint> endpoints, String title) {
           itemEndpoint(
               endpoint.getMethod().toString(), endpoint.getPathUrl(), endpoint.getSummary()));
     }
-    return sb.append(System.lineSeparator()).toString();
+
+    safelyAppend(outputStreamWriter, sb.append(System.lineSeparator()).toString());
   }
 
   private String itemEndpoint(String method, String path, String desc) {
@@ -318,8 +321,7 @@ public String title(String title, char ch) {
         separator(ch), little, StringUtils.center(title, LINE_LENGTH - 4), little, separator(ch));
   }
 
-  public StringBuilder separator(char ch) {
-    StringBuilder sb = new StringBuilder();
-    return sb.append(StringUtils.repeat(ch, LINE_LENGTH)).append(System.lineSeparator());
+  public String separator(char ch) {
+    return StringUtils.repeat(ch, LINE_LENGTH) + System.lineSeparator();
   }
 }
diff --git a/core/src/main/java/org/openapitools/openapidiff/core/output/HtmlRender.java b/core/src/main/java/org/openapitools/openapidiff/core/output/HtmlRender.java
index 0513424cd..eb4c5b884 100644
--- a/core/src/main/java/org/openapitools/openapidiff/core/output/HtmlRender.java
+++ b/core/src/main/java/org/openapitools/openapidiff/core/output/HtmlRender.java
@@ -26,16 +26,21 @@
 import io.swagger.v3.oas.models.media.Schema;
 import io.swagger.v3.oas.models.parameters.Parameter;
 import io.swagger.v3.oas.models.responses.ApiResponse;
+import io.swagger.v3.oas.models.security.SecurityRequirement;
+import j2html.rendering.FlatHtml;
 import j2html.tags.ContainerTag;
 import j2html.tags.specialized.DivTag;
 import j2html.tags.specialized.HtmlTag;
 import j2html.tags.specialized.LiTag;
 import j2html.tags.specialized.OlTag;
 import j2html.tags.specialized.UlTag;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
 import java.util.Optional;
+import org.openapitools.openapidiff.core.exception.RendererException;
 import org.openapitools.openapidiff.core.model.ChangedApiResponse;
 import org.openapitools.openapidiff.core.model.ChangedContent;
 import org.openapitools.openapidiff.core.model.ChangedMediaType;
@@ -46,6 +51,8 @@
 import org.openapitools.openapidiff.core.model.ChangedParameters;
 import org.openapitools.openapidiff.core.model.ChangedResponse;
 import org.openapitools.openapidiff.core.model.ChangedSchema;
+import org.openapitools.openapidiff.core.model.ChangedSecurityRequirement;
+import org.openapitools.openapidiff.core.model.ChangedSecurityRequirements;
 import org.openapitools.openapidiff.core.model.DiffContext;
 import org.openapitools.openapidiff.core.model.DiffResult;
 import org.openapitools.openapidiff.core.model.Endpoint;
@@ -60,18 +67,30 @@ public class HtmlRender implements Render {
 
   private final String title;
   private final String linkCss;
+  private final boolean showAllChanges;
   protected ChangedOpenApi diff;
 
   public HtmlRender() {
     this("Api Change Log", "http://deepoove.com/swagger-diff/stylesheets/demo.css");
   }
 
+  public HtmlRender(boolean showAllChanges) {
+    this("Api Change Log", "http://deepoove.com/swagger-diff/stylesheets/demo.css", showAllChanges);
+  }
+
   public HtmlRender(String title, String linkCss) {
     this.title = title;
     this.linkCss = linkCss;
+    this.showAllChanges = false;
   }
 
-  public String render(ChangedOpenApi diff) {
+  public HtmlRender(String title, String linkCss, boolean showAllChanges) {
+    this.title = title;
+    this.linkCss = linkCss;
+    this.showAllChanges = showAllChanges;
+  }
+
+  public void render(ChangedOpenApi diff, OutputStreamWriter outputStreamWriter) {
     this.diff = diff;
 
     List<Endpoint> newEndpoints = diff.getNewEndpoints();
@@ -86,10 +105,16 @@ public String render(ChangedOpenApi diff) {
     List<ChangedOperation> changedOperations = diff.getChangedOperations();
     OlTag ol_changed = ol_changed(changedOperations);
 
-    return renderHtml(ol_newEndpoint, ol_missingEndpoint, ol_deprecatedEndpoint, ol_changed);
+    renderHtml(
+        ol_newEndpoint, ol_missingEndpoint, ol_deprecatedEndpoint, ol_changed, outputStreamWriter);
   }
 
-  public String renderHtml(OlTag ol_new, OlTag ol_miss, OlTag ol_deprec, OlTag ol_changed) {
+  public void renderHtml(
+      OlTag ol_new,
+      OlTag ol_miss,
+      OlTag ol_deprec,
+      OlTag ol_changed,
+      OutputStreamWriter outputStreamWriter) {
     HtmlTag html =
         html()
             .attr("lang", "en")
@@ -110,7 +135,14 @@ public String renderHtml(OlTag ol_new, OlTag ol_miss, OlTag ol_deprec, OlTag ol_
                                 div().with(h2("What's Deprecated"), hr(), ol_deprec),
                                 div().with(h2("What's Changed"), hr(), ol_changed))));
 
-    return document().render() + html.render();
+    try {
+      FlatHtml<OutputStreamWriter> flatHtml = FlatHtml.into(outputStreamWriter);
+      document().render(flatHtml);
+      html.render(flatHtml);
+      outputStreamWriter.close();
+    } catch (IOException e) {
+      throw new RendererException("Problem rendering html document.", e);
+    }
   }
 
   private OlTag ol_newEndpoint(List<Endpoint> endpoints) {
@@ -183,6 +215,11 @@ private OlTag ol_changed(List<ChangedOperation> changedOperations) {
         ul_detail.with(
             li().with(h3("Response")).with(ul_response(changedOperation.getApiResponses())));
       }
+      if (showAllChanges && changedOperation.resultSecurityRequirements().isDifferent()) {
+        ul_detail.with(
+            li().with(h3("Security Requirements"))
+                .with(ul_securityRequirements(changedOperation.getSecurityRequirements())));
+      }
       ol.with(
           li().with(span(method).withClass(method))
               .withText(pathUrl + " ")
@@ -192,6 +229,52 @@ private OlTag ol_changed(List<ChangedOperation> changedOperations) {
     return ol;
   }
 
+  private UlTag ul_securityRequirements(ChangedSecurityRequirements changedSecurityRequirements) {
+    List<SecurityRequirement> addRequirements = changedSecurityRequirements.getIncreased();
+    List<SecurityRequirement> delRequirements = changedSecurityRequirements.getMissing();
+    List<ChangedSecurityRequirement> changedRequirements = changedSecurityRequirements.getChanged();
+    UlTag ul = ul().withClass("change security requirements");
+    if (addRequirements != null) {
+      for (SecurityRequirement addRequirement : addRequirements) {
+        ul.with(li_addSecurityRequirement(addRequirement));
+      }
+    }
+    if (delRequirements != null) {
+      for (SecurityRequirement delRequirement : delRequirements) {
+        ul.with(li_missingSecurityRequirement(delRequirement));
+      }
+    }
+    if (changedRequirements != null) {
+      for (ChangedSecurityRequirement changedRequirement : changedRequirements) {
+        ul.with(li_changedSecurityRequirement(changedRequirement));
+      }
+    }
+
+    return ul;
+  }
+
+  private LiTag li_addSecurityRequirement(SecurityRequirement securityRequirement) {
+    return li().withText("New security requirement : ")
+        .with(span(null == securityRequirement.toString() ? "" : (securityRequirement.toString())));
+  }
+
+  private LiTag li_missingSecurityRequirement(SecurityRequirement securityRequirement) {
+    return li().withText("Deleted security requirement : ")
+        .with(span(null == securityRequirement.toString() ? "" : (securityRequirement.toString())));
+  }
+
+  private LiTag li_changedSecurityRequirement(
+      ChangedSecurityRequirement changedSecurityRequirement) {
+    return li().withText(String.format("Changed security requirement : "))
+        .with(
+            span(
+                (null == changedSecurityRequirement.getNewSecurityRequirement()
+                        || null
+                            == changedSecurityRequirement.getNewSecurityRequirement().toString())
+                    ? ""
+                    : (changedSecurityRequirement.getNewSecurityRequirement().toString())));
+  }
+
   private UlTag ul_response(ChangedApiResponse changedApiResponse) {
     Map<String, ApiResponse> addResponses = changedApiResponse.getIncreased();
     Map<String, ApiResponse> delResponses = changedApiResponse.getMissing();
@@ -262,8 +345,10 @@ private LiTag li_changedRequest(String name, ChangedMediaType request) {
     LiTag li =
         li().with(div_changedSchema(request.getSchema()))
             .withText(String.format("Changed body: '%s'", name));
-    if (request.isIncompatible()) {
+    if (request.isIncompatible() && !showAllChanges) {
       incompatibilities(li, request.getSchema());
+    } else if (showAllChanges) {
+      allChanges(li, request.getSchema());
     }
     return li;
   }
@@ -274,6 +359,28 @@ private DivTag div_changedSchema(ChangedSchema schema) {
     return div;
   }
 
+  private void allChanges(final LiTag output, final ChangedSchema schema) {
+    allChanges(output, "", schema);
+  }
+
+  private void allChanges(
+      final ContainerTag<?> output, String propName, final ChangedSchema schema) {
+    String prefix = propName.isEmpty() ? "" : propName + ".";
+    properties(
+        output, prefix, "Missing property", schema.getMissingProperties(), schema.getContext());
+    properties(
+        output, prefix, "Added property", schema.getIncreasedProperties(), schema.getContext());
+
+    propertiesChanged(
+        output, prefix, "Changed property", schema.getChangedProperties(), schema.getContext());
+    if (schema.getItems() != null) {
+      itemsAllChanges(output, propName, schema.getItems());
+    }
+    schema
+        .getChangedProperties()
+        .forEach((name, property) -> allChanges(output, prefix + name, property));
+  }
+
   private void incompatibilities(final LiTag output, final ChangedSchema schema) {
     incompatibilities(output, "", schema);
   }
@@ -299,6 +406,10 @@ private void items(ContainerTag<?> output, String propName, ChangedSchema schema
     incompatibilities(output, propName + "[n]", schema);
   }
 
+  private void itemsAllChanges(ContainerTag<?> output, String propName, ChangedSchema schema) {
+    allChanges(output, propName + "[n]", schema);
+  }
+
   private void properties(
       ContainerTag<?> output,
       String propPrefix,
@@ -310,6 +421,17 @@ private void properties(
     }
   }
 
+  private void propertiesChanged(
+      ContainerTag<?> output,
+      String propPrefix,
+      String title,
+      Map<String, ChangedSchema> properties,
+      DiffContext context) {
+    if (properties != null) {
+      properties.forEach((key, value) -> resolveProperty(output, propPrefix, key, value, title));
+    }
+  }
+
   private void resolveProperty(
       ContainerTag<?> output, String propPrefix, String key, Schema<?> value, String title) {
     try {
@@ -319,6 +441,15 @@ private void resolveProperty(
     }
   }
 
+  private void resolveProperty(
+      ContainerTag<?> output, String propPrefix, String key, ChangedSchema value, String title) {
+    try {
+      property(output, propPrefix + key, title, resolve(value));
+    } catch (Exception e) {
+      property(output, propPrefix + key, title, type(value));
+    }
+  }
+
   protected void property(ContainerTag<?> output, String name, String title, Schema<?> schema) {
     property(output, name, title, type(schema));
   }
@@ -332,6 +463,13 @@ protected Schema<?> resolve(Schema<?> schema) {
         diff.getNewSpecOpenApi().getComponents(), schema, schema.get$ref());
   }
 
+  protected Schema<?> resolve(ChangedSchema schema) {
+    return refPointer.resolveRef(
+        diff.getNewSpecOpenApi().getComponents(),
+        schema.getNewSchema(),
+        schema.getNewSchema().get$ref());
+  }
+
   protected String type(Schema<?> schema) {
     String result = "object";
     if (schema == null) {
@@ -344,6 +482,10 @@ protected String type(Schema<?> schema) {
     return result;
   }
 
+  protected String type(ChangedSchema schema) {
+    return type(schema.getNewSchema());
+  }
+
   private UlTag ul_param(ChangedParameters changedParameters) {
     List<Parameter> addParameters = changedParameters.getIncreased();
     List<Parameter> delParameters = changedParameters.getMissing();
@@ -400,10 +542,14 @@ private LiTag li_changedParam(ChangedParameter changeParam) {
             .map(ChangedMetadata::isDifferent)
             .orElse(false);
     Parameter rightParam = changeParam.getNewParameter();
-    Parameter leftParam = changeParam.getNewParameter();
+    Parameter leftParam = changeParam.getOldParameter();
     LiTag li = li().withText(changeParam.getName() + " in " + changeParam.getIn());
     if (changeRequired) {
-      li.withText(" change into " + (rightParam.getRequired() ? "required" : "not required"));
+      li.withText(
+          " change into "
+              + (rightParam.getRequired() != null && rightParam.getRequired()
+                  ? "required"
+                  : "not required"));
     }
     if (changeDescription) {
       li.withText(" Notes ")
diff --git a/core/src/main/java/org/openapitools/openapidiff/core/output/HttpStatus.java b/core/src/main/java/org/openapitools/openapidiff/core/output/HttpStatus.java
new file mode 100644
index 000000000..13a81c324
--- /dev/null
+++ b/core/src/main/java/org/openapitools/openapidiff/core/output/HttpStatus.java
@@ -0,0 +1,112 @@
+/*
+ * $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//httpclient/src/java/org/apache/commons/httpclient/HttpStatus.java,v 1.18 2004/05/02 11:21:13 olegk Exp $
+ * $Revision: 480424 $
+ * $Date: 2006-11-29 06:56:49 +0100 (Wed, 29 Nov 2006) $
+ *
+ * ====================================================================
+ *
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+package org.openapitools.openapidiff.core.output;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Constants enumerating the HTTP status codes. All status codes defined in RFC1945 (HTTP/1.0,
+ * RFC2616 (HTTP/1.1), and RFC2518 (WebDAV) are supported.
+ */
+public final class HttpStatus {
+
+  private static final Map<Integer, String> REASON_PHRASES = new HashMap<>();
+
+  static {
+    REASON_PHRASES.put(100, "Continue");
+    REASON_PHRASES.put(101, "Switching Protocols");
+    REASON_PHRASES.put(102, "Processing");
+    REASON_PHRASES.put(200, "OK");
+    REASON_PHRASES.put(201, "Created");
+    REASON_PHRASES.put(202, "Accepted");
+    REASON_PHRASES.put(203, "Non Authoritative Information");
+    REASON_PHRASES.put(204, "No Content");
+    REASON_PHRASES.put(205, "Reset Content");
+    REASON_PHRASES.put(206, "Partial Content");
+    REASON_PHRASES.put(207, "Multi-Status");
+    REASON_PHRASES.put(300, "Multiple Choices");
+    REASON_PHRASES.put(301, "Moved Permanently");
+    REASON_PHRASES.put(302, "Moved Temporarily");
+    REASON_PHRASES.put(303, "See Other");
+    REASON_PHRASES.put(304, "Not Modified");
+    REASON_PHRASES.put(305, "Use Proxy");
+    REASON_PHRASES.put(307, "Temporary Redirect");
+    REASON_PHRASES.put(400, "Bad Request");
+    REASON_PHRASES.put(401, "Unauthorized");
+    REASON_PHRASES.put(402, "Payment Required");
+    REASON_PHRASES.put(403, "Forbidden");
+    REASON_PHRASES.put(404, "Not Found");
+    REASON_PHRASES.put(405, "Method Not Allowed");
+    REASON_PHRASES.put(406, "Not Acceptable");
+    REASON_PHRASES.put(407, "Proxy Authentication Required");
+    REASON_PHRASES.put(408, "Request Timeout");
+    REASON_PHRASES.put(409, "Conflict");
+    REASON_PHRASES.put(410, "Gone");
+    REASON_PHRASES.put(411, "Length Required");
+    REASON_PHRASES.put(412, "Precondition Failed");
+    REASON_PHRASES.put(413, "Request Too Long");
+    REASON_PHRASES.put(414, "Request-URI Too Long");
+    REASON_PHRASES.put(415, "Unsupported Media Type");
+    REASON_PHRASES.put(416, "Requested Range Not Satisfiable");
+    REASON_PHRASES.put(417, "Expectation Failed");
+    REASON_PHRASES.put(419, "Insufficient Space On Resource");
+    REASON_PHRASES.put(420, "Method Failure");
+    REASON_PHRASES.put(422, "Unprocessable Entity");
+    REASON_PHRASES.put(423, "Locked");
+    REASON_PHRASES.put(424, "Failed Dependency");
+    REASON_PHRASES.put(500, "Internal Server Error");
+    REASON_PHRASES.put(501, "Not Implemented");
+    REASON_PHRASES.put(502, "Bad Gateway");
+    REASON_PHRASES.put(503, "Service Unavailable");
+    REASON_PHRASES.put(504, "Gateway Timeout");
+    REASON_PHRASES.put(505, "Http Version Not Supported");
+    REASON_PHRASES.put(507, "Insufficient Storage");
+  }
+
+  /**
+   * Get the reason phrase for a particular status code.
+   *
+   * <p>This method always returns the English text as specified in the relevant RFCs and is not
+   * internationalized.
+   *
+   * @param statusCode the numeric status code
+   * @return the reason phrase associated with the given status code or null if the status code is
+   *     not recognized.
+   */
+  public static String getReasonPhrase(int statusCode) {
+    if (statusCode < 0) {
+      throw new IllegalArgumentException("status code may not be negative");
+    }
+    return REASON_PHRASES.get(statusCode);
+  }
+
+  private HttpStatus() {}
+}
diff --git a/core/src/main/java/org/openapitools/openapidiff/core/output/JsonRender.java b/core/src/main/java/org/openapitools/openapidiff/core/output/JsonRender.java
index b0518d000..b6629d5f9 100644
--- a/core/src/main/java/org/openapitools/openapidiff/core/output/JsonRender.java
+++ b/core/src/main/java/org/openapitools/openapidiff/core/output/JsonRender.java
@@ -1,18 +1,31 @@
 package org.openapitools.openapidiff.core.output;
 
+import com.fasterxml.jackson.annotation.JsonInclude;
 import com.fasterxml.jackson.core.JsonProcessingException;
 import com.fasterxml.jackson.databind.ObjectMapper;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import org.openapitools.openapidiff.core.exception.RendererException;
 import org.openapitools.openapidiff.core.model.ChangedOpenApi;
 
 public class JsonRender implements Render {
-  private final ObjectMapper objectMapper = new ObjectMapper();
+  private final ObjectMapper objectMapper;
+
+  public JsonRender() {
+    objectMapper = new ObjectMapper();
+    objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
+    objectMapper.findAndRegisterModules();
+  }
 
   @Override
-  public String render(ChangedOpenApi diff) {
+  public void render(ChangedOpenApi diff, OutputStreamWriter outputStreamWriter) {
     try {
-      return objectMapper.writeValueAsString(diff);
+      objectMapper.writeValue(outputStreamWriter, diff);
+      outputStreamWriter.close();
     } catch (JsonProcessingException e) {
-      throw new RuntimeException("Could not serialize diff as JSON", e);
+      throw new RendererException("Could not serialize diff as JSON", e);
+    } catch (IOException e) {
+      throw new RendererException(e);
     }
   }
 }
diff --git a/core/src/main/java/org/openapitools/openapidiff/core/output/MarkdownRender.java b/core/src/main/java/org/openapitools/openapidiff/core/output/MarkdownRender.java
index fd6726615..e55179b15 100644
--- a/core/src/main/java/org/openapitools/openapidiff/core/output/MarkdownRender.java
+++ b/core/src/main/java/org/openapitools/openapidiff/core/output/MarkdownRender.java
@@ -11,11 +11,16 @@
 import io.swagger.v3.oas.models.media.Schema;
 import io.swagger.v3.oas.models.parameters.Parameter;
 import io.swagger.v3.oas.models.responses.ApiResponse;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
-import org.apache.commons.httpclient.HttpStatus;
+import java.util.Set;
 import org.apache.commons.lang3.StringUtils;
+import org.openapitools.openapidiff.core.exception.RendererException;
 import org.openapitools.openapidiff.core.model.*;
+import org.openapitools.openapidiff.core.model.schema.ChangedOneOfSchema;
 import org.openapitools.openapidiff.core.utils.RefPointer;
 import org.openapitools.openapidiff.core.utils.RefType;
 import org.slf4j.Logger;
@@ -36,33 +41,40 @@ public class MarkdownRender implements Render {
 
   protected RefPointer<Schema<?>> refPointer = new RefPointer<>(RefType.SCHEMAS);
   protected ChangedOpenApi diff;
+  protected Set<Schema<?>> handledSchemas = new HashSet<>();
   /**
    * A parameter which indicates whether or not metadata (summary and metadata) changes should be
    * logged in the changelog file.
    */
   protected boolean showChangedMetadata;
 
-  public String render(ChangedOpenApi diff) {
+  public void render(ChangedOpenApi diff, OutputStreamWriter outputStreamWriter) {
     this.diff = diff;
-    return listEndpoints("What's New", diff.getNewEndpoints())
-        + listEndpoints("What's Deleted", diff.getMissingEndpoints())
-        + listEndpoints("What's Deprecated", diff.getDeprecatedEndpoints())
-        + listEndpoints(diff.getChangedOperations());
+    this.handledSchemas.clear();
+    listEndpoints("What's New", diff.getNewEndpoints(), outputStreamWriter);
+    listEndpoints("What's Deleted", diff.getMissingEndpoints(), outputStreamWriter);
+    listEndpoints("What's Deprecated", diff.getDeprecatedEndpoints(), outputStreamWriter);
+    listEndpoints(diff.getChangedOperations(), outputStreamWriter);
+    try {
+      outputStreamWriter.close();
+    } catch (IOException e) {
+      throw new RendererException(e);
+    }
   }
 
   protected String sectionTitle(String title) {
     return H4 + title + '\n' + HR + '\n';
   }
 
-  protected String listEndpoints(String title, List<Endpoint> endpoints) {
+  protected void listEndpoints(
+      String title, List<Endpoint> endpoints, OutputStreamWriter outputStreamWriter) {
     if (null == endpoints || endpoints.isEmpty()) {
-      return "";
+      return;
     }
-    StringBuilder sb = new StringBuilder(sectionTitle(title));
+    safelyAppend(outputStreamWriter, sectionTitle(title));
     endpoints.stream()
         .map(e -> itemEndpoint(e.getMethod().toString(), e.getPathUrl(), e.getSummary()))
-        .forEach(sb::append);
-    return sb.toString();
+        .forEach(csq -> safelyAppend(outputStreamWriter, csq));
   }
 
   protected String itemEndpoint(String method, String path, String summary) {
@@ -77,41 +89,36 @@ protected String titleH5(String title) {
     return H6 + title + '\n';
   }
 
-  protected String listEndpoints(List<ChangedOperation> changedOperations) {
+  protected void listEndpoints(
+      List<ChangedOperation> changedOperations, OutputStreamWriter outputStreamWriter) {
     if (null == changedOperations || changedOperations.isEmpty()) {
-      return "";
+      return;
     }
-    StringBuilder sb = new StringBuilder(sectionTitle("What's Changed"));
-    changedOperations.stream()
-        .map(
-            operation -> {
-              StringBuilder details =
-                  new StringBuilder()
-                      .append(
-                          itemEndpoint(
-                              operation.getHttpMethod().toString(),
-                              operation.getPathUrl(),
-                              operation.getSummary()));
-              if (result(operation.getParameters()).isDifferent()) {
-                details
-                    .append(titleH5("Parameters:"))
-                    .append(parameters(operation.getParameters()));
-              }
-              if (operation.resultRequestBody().isDifferent()) {
-                details
-                    .append(titleH5("Request:"))
-                    .append(metadata("Description", operation.getRequestBody().getDescription()))
-                    .append(bodyContent(operation.getRequestBody().getContent()));
-              }
-              if (operation.resultApiResponses().isDifferent()) {
-                details
-                    .append(titleH5("Return Type:"))
-                    .append(responses(operation.getApiResponses()));
-              }
-              return details.toString();
-            })
-        .forEach(sb::append);
-    return sb.toString();
+    safelyAppend(outputStreamWriter, sectionTitle("What's Changed"));
+    changedOperations.forEach(
+        operation -> {
+          safelyAppend(
+              outputStreamWriter,
+              itemEndpoint(
+                  operation.getHttpMethod().toString(),
+                  operation.getPathUrl(),
+                  operation.getSummary()));
+          if (result(operation.getParameters()).isDifferent()) {
+            safelyAppend(outputStreamWriter, titleH5("Parameters:"));
+            safelyAppend(outputStreamWriter, parameters(operation.getParameters()));
+          }
+          if (operation.resultRequestBody().isDifferent()) {
+            safelyAppend(outputStreamWriter, titleH5("Request:"));
+            safelyAppend(
+                outputStreamWriter,
+                metadata("Description", operation.getRequestBody().getDescription()));
+            safelyAppend(outputStreamWriter, bodyContent(operation.getRequestBody().getContent()));
+          }
+          if (operation.resultApiResponses().isDifferent()) {
+            safelyAppend(outputStreamWriter, titleH5("Return Type:"));
+            safelyAppend(outputStreamWriter, responses(operation.getApiResponses()));
+          }
+        });
   }
 
   protected String responses(ChangedApiResponse changedApiResponse) {
@@ -155,8 +162,8 @@ protected String itemResponse(String code, ChangedResponse response) {
   protected String itemResponse(String title, String code, String description) {
     StringBuilder sb = new StringBuilder();
     String status = "";
-    if (!code.equals("default")) {
-      status = HttpStatus.getStatusText(Integer.parseInt(code));
+    if (!code.equals("default") && !code.matches("[1-5]XX")) {
+      status = HttpStatus.getReasonPhrase(Integer.parseInt(code));
     }
     sb.append(format("%s : **%s %s**\n", title, code, status));
     sb.append(metadata(description));
@@ -336,6 +343,8 @@ protected String schema(int deepness, ComposedSchema schema, DiffContext context
   }
 
   protected String schema(int deepness, Schema schema, DiffContext context) {
+    if (handledSchemas.contains(schema)) return "";
+    handledSchemas.add(schema);
     StringBuilder sb = new StringBuilder();
     sb.append(listItem(deepness, "Enum", schema.getEnum()));
     sb.append(properties(deepness, "Property", schema.getProperties(), true, context));
@@ -523,7 +532,7 @@ protected String blockquote(String beginning) {
 
   protected String blockquote(String beginning, String text) {
     String blockquote = blockquote(beginning);
-    return blockquote + text.trim().replace("\n", "\n" + blockquote) + '\n';
+    return blockquote + text.trim().replace("\n", "\n" + blockquote) + "\n\n";
   }
 
   protected String type(Schema<?> schema) {
diff --git a/core/src/main/java/org/openapitools/openapidiff/core/output/Render.java b/core/src/main/java/org/openapitools/openapidiff/core/output/Render.java
index 4a48ebcfa..529a290b9 100644
--- a/core/src/main/java/org/openapitools/openapidiff/core/output/Render.java
+++ b/core/src/main/java/org/openapitools/openapidiff/core/output/Render.java
@@ -1,8 +1,51 @@
 package org.openapitools.openapidiff.core.output;
 
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import org.openapitools.openapidiff.core.exception.RendererException;
 import org.openapitools.openapidiff.core.model.ChangedOpenApi;
 
 public interface Render {
 
-  String render(ChangedOpenApi diff);
+  /**
+   * render provided diff object
+   *
+   * @param diff diff object to render
+   * @param outputStreamWriter writer for rendered results
+   */
+  void render(ChangedOpenApi diff, OutputStreamWriter outputStreamWriter) throws RendererException;
+
+  /**
+   * render provided diff object
+   *
+   * @deprecated since 2.1.0, use {@link Render#render(ChangedOpenApi, OutputStreamWriter)} to avoid
+   *     massive String output issues. details <a
+   *     href="https://github.com/OpenAPITools/openapi-diff/issues/543">#543</a>
+   * @param diff diff object to render
+   * @return rendered output
+   */
+  @Deprecated
+  default String render(ChangedOpenApi diff) throws RendererException {
+    ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
+    OutputStreamWriter outputStreamWriter = new OutputStreamWriter(byteArrayOutputStream);
+    render(diff, outputStreamWriter);
+    String result = byteArrayOutputStream.toString();
+
+    try {
+      outputStreamWriter.close();
+    } catch (IOException e) {
+      throw new RendererException(e);
+    }
+
+    return result;
+  }
+
+  default void safelyAppend(OutputStreamWriter outputStreamWriter, String csq) {
+    try {
+      outputStreamWriter.append(csq);
+    } catch (IOException ex) {
+      throw new RendererException(ex);
+    }
+  }
 }
diff --git a/core/src/main/java/org/openapitools/openapidiff/core/utils/ChangedUtils.java b/core/src/main/java/org/openapitools/openapidiff/core/utils/ChangedUtils.java
index 27a5e0e20..6da806a96 100644
--- a/core/src/main/java/org/openapitools/openapidiff/core/utils/ChangedUtils.java
+++ b/core/src/main/java/org/openapitools/openapidiff/core/utils/ChangedUtils.java
@@ -5,7 +5,9 @@
 
 public class ChangedUtils {
 
-  private ChangedUtils() {}
+  private ChangedUtils() {
+    throw new UnsupportedOperationException("Utility class. Do not instantiate");
+  }
 
   public static boolean isUnchanged(Changed changed) {
     return changed == null || changed.isUnchanged();
diff --git a/core/src/main/java/org/openapitools/openapidiff/core/utils/Copy.java b/core/src/main/java/org/openapitools/openapidiff/core/utils/Copy.java
index 4179fd359..73c9a073b 100644
--- a/core/src/main/java/org/openapitools/openapidiff/core/utils/Copy.java
+++ b/core/src/main/java/org/openapitools/openapidiff/core/utils/Copy.java
@@ -5,7 +5,9 @@
 
 public class Copy {
 
-  private Copy() {}
+  private Copy() {
+    throw new UnsupportedOperationException("Utility class. Do not instantiate");
+  }
 
   public static <K, V> Map<K, V> copyMap(Map<K, V> map) {
     if (map == null) {
diff --git a/core/src/main/java/org/openapitools/openapidiff/core/utils/EndpointUtils.java b/core/src/main/java/org/openapitools/openapidiff/core/utils/EndpointUtils.java
index 749b0bdbd..c749d8a54 100644
--- a/core/src/main/java/org/openapitools/openapidiff/core/utils/EndpointUtils.java
+++ b/core/src/main/java/org/openapitools/openapidiff/core/utils/EndpointUtils.java
@@ -10,7 +10,9 @@
 
 public class EndpointUtils {
 
-  private EndpointUtils() {}
+  private EndpointUtils() {
+    throw new UnsupportedOperationException("Utility class. Do not instantiate");
+  }
 
   public static Collection<Endpoint> convert2Endpoints(
       String pathUrl, Map<PathItem.HttpMethod, Operation> map) {
diff --git a/core/src/main/java/org/openapitools/openapidiff/core/utils/FileUtils.java b/core/src/main/java/org/openapitools/openapidiff/core/utils/FileUtils.java
new file mode 100644
index 000000000..a2c8b147e
--- /dev/null
+++ b/core/src/main/java/org/openapitools/openapidiff/core/utils/FileUtils.java
@@ -0,0 +1,35 @@
+package org.openapitools.openapidiff.core.utils;
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import org.openapitools.openapidiff.core.model.ChangedOpenApi;
+import org.openapitools.openapidiff.core.output.Render;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public final class FileUtils {
+  private static final Logger logger = LoggerFactory.getLogger(FileUtils.class);
+
+  private FileUtils() {
+    throw new UnsupportedOperationException("Utility class. Do not instantiate");
+  }
+
+  public static void writeToFile(
+      final Render render, final ChangedOpenApi diff, final String fileName) {
+    if (fileName == null || fileName.isEmpty()) {
+      logger.debug("File name cannot be null or empty.");
+      return;
+    }
+
+    final Path filePath = Paths.get(fileName);
+    try (final FileOutputStream outputStream = new FileOutputStream(filePath.toFile());
+        final OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream)) {
+      render.render(diff, outputStreamWriter);
+    } catch (final IOException e) {
+      logger.error("Exception while writing to file {}", fileName, e);
+    }
+  }
+}
diff --git a/core/src/test/java/org/openapitools/openapidiff/core/AddPropDiffTest.java b/core/src/test/java/org/openapitools/openapidiff/core/AddPropDiffTest.java
deleted file mode 100644
index b71d8f039..000000000
--- a/core/src/test/java/org/openapitools/openapidiff/core/AddPropDiffTest.java
+++ /dev/null
@@ -1,21 +0,0 @@
-package org.openapitools.openapidiff.core;
-
-import static org.openapitools.openapidiff.core.TestUtils.assertOpenApiAreEquals;
-import static org.openapitools.openapidiff.core.TestUtils.assertOpenApiBackwardIncompatible;
-
-import org.junit.jupiter.api.Test;
-
-public class AddPropDiffTest {
-  private final String OPENAPI_DOC1 = "add-prop-1.yaml";
-  private final String OPENAPI_DOC2 = "add-prop-2.yaml";
-
-  @Test
-  public void testDiffSame() {
-    assertOpenApiAreEquals(OPENAPI_DOC1, OPENAPI_DOC1);
-  }
-
-  @Test
-  public void testDiffDifferent() {
-    assertOpenApiBackwardIncompatible(OPENAPI_DOC1, OPENAPI_DOC2);
-  }
-}
diff --git a/core/src/test/java/org/openapitools/openapidiff/core/AddPropPutDiffTest.java b/core/src/test/java/org/openapitools/openapidiff/core/AddPropPutDiffTest.java
deleted file mode 100644
index d73ab6ebe..000000000
--- a/core/src/test/java/org/openapitools/openapidiff/core/AddPropPutDiffTest.java
+++ /dev/null
@@ -1,21 +0,0 @@
-package org.openapitools.openapidiff.core;
-
-import static org.openapitools.openapidiff.core.TestUtils.assertOpenApiAreEquals;
-import static org.openapitools.openapidiff.core.TestUtils.assertOpenApiBackwardIncompatible;
-
-import org.junit.jupiter.api.Test;
-
-public class AddPropPutDiffTest {
-  private final String OPENAPI_DOC1 = "add-prop-put-1.yaml";
-  private final String OPENAPI_DOC2 = "add-prop-put-2.yaml";
-
-  @Test
-  public void testDiffSame() {
-    assertOpenApiAreEquals(OPENAPI_DOC1, OPENAPI_DOC1);
-  }
-
-  @Test
-  public void testDiffDifferent() {
-    assertOpenApiBackwardIncompatible(OPENAPI_DOC1, OPENAPI_DOC2);
-  }
-}
diff --git a/core/src/test/java/org/openapitools/openapidiff/core/AdditionalPropertiesTest.java b/core/src/test/java/org/openapitools/openapidiff/core/AdditionalPropertiesTest.java
deleted file mode 100644
index df7885e08..000000000
--- a/core/src/test/java/org/openapitools/openapidiff/core/AdditionalPropertiesTest.java
+++ /dev/null
@@ -1,16 +0,0 @@
-package org.openapitools.openapidiff.core;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-import org.junit.jupiter.api.Test;
-import org.openapitools.openapidiff.core.model.ChangedOpenApi;
-import org.openapitools.openapidiff.core.output.ConsoleRender;
-
-class AdditionalPropertiesTest {
-  @Test
-  void booleanAdditionalPropertiesAreSupported() {
-    ChangedOpenApi diff = OpenApiCompare.fromLocations("issue-256_1.json", "issue-256_2.json");
-    ConsoleRender render = new ConsoleRender();
-    assertThat(render.render(diff)).isNotBlank();
-  }
-}
diff --git a/core/src/test/java/org/openapitools/openapidiff/core/AllOfOneOfDiffTest.java b/core/src/test/java/org/openapitools/openapidiff/core/AllOfOneOfDiffTest.java
new file mode 100644
index 000000000..ec1bcfc53
--- /dev/null
+++ b/core/src/test/java/org/openapitools/openapidiff/core/AllOfOneOfDiffTest.java
@@ -0,0 +1,14 @@
+package org.openapitools.openapidiff.core;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import org.junit.jupiter.api.Test;
+import org.openapitools.openapidiff.core.model.ChangedOpenApi;
+
+public class AllOfOneOfDiffTest {
+  @Test
+  void allOfReferringToOneOfSchemasAreSupported() {
+    ChangedOpenApi diff = OpenApiCompare.fromLocations("issue-317_1.json", "issue-317_2.json");
+    assertThat(diff.isCoreChanged().isUnchanged());
+  }
+}
diff --git a/core/src/test/java/org/openapitools/openapidiff/core/BackwardCompatibilityTest.java b/core/src/test/java/org/openapitools/openapidiff/core/BackwardCompatibilityTest.java
deleted file mode 100644
index 612700ca7..000000000
--- a/core/src/test/java/org/openapitools/openapidiff/core/BackwardCompatibilityTest.java
+++ /dev/null
@@ -1,49 +0,0 @@
-package org.openapitools.openapidiff.core;
-
-import static org.openapitools.openapidiff.core.TestUtils.assertOpenApiBackwardCompatible;
-import static org.openapitools.openapidiff.core.TestUtils.assertOpenApiBackwardIncompatible;
-
-import org.junit.jupiter.api.Test;
-
-public class BackwardCompatibilityTest {
-  private final String OPENAPI_DOC1 = "backwardCompatibility/bc_1.yaml";
-  private final String OPENAPI_DOC2 = "backwardCompatibility/bc_2.yaml";
-  private final String OPENAPI_DOC3 = "backwardCompatibility/bc_3.yaml";
-  private final String OPENAPI_DOC4 = "backwardCompatibility/bc_4.yaml";
-  private final String OPENAPI_DOC5 = "backwardCompatibility/bc_5.yaml";
-
-  @Test
-  public void testNoChange() {
-    assertOpenApiBackwardCompatible(OPENAPI_DOC1, OPENAPI_DOC1, false);
-  }
-
-  @Test
-  public void testApiAdded() {
-    assertOpenApiBackwardCompatible(OPENAPI_DOC1, OPENAPI_DOC2, true);
-  }
-
-  @Test
-  public void testApiMissing() {
-    assertOpenApiBackwardIncompatible(OPENAPI_DOC2, OPENAPI_DOC1);
-  }
-
-  @Test
-  public void testApiChangedOperationAdded() {
-    assertOpenApiBackwardCompatible(OPENAPI_DOC2, OPENAPI_DOC3, true);
-  }
-
-  @Test
-  public void testApiChangedOperationMissing() {
-    assertOpenApiBackwardIncompatible(OPENAPI_DOC3, OPENAPI_DOC2);
-  }
-
-  @Test
-  public void testApiOperationChanged() {
-    assertOpenApiBackwardCompatible(OPENAPI_DOC2, OPENAPI_DOC4, true);
-  }
-
-  @Test
-  public void testApiReadWriteOnlyPropertiesChanged() {
-    assertOpenApiBackwardCompatible(OPENAPI_DOC1, OPENAPI_DOC5, true);
-  }
-}
diff --git a/core/src/test/java/org/openapitools/openapidiff/core/ChangesResolver.java b/core/src/test/java/org/openapitools/openapidiff/core/ChangesResolver.java
new file mode 100644
index 000000000..cb0fe37b6
--- /dev/null
+++ b/core/src/test/java/org/openapitools/openapidiff/core/ChangesResolver.java
@@ -0,0 +1,113 @@
+package org.openapitools.openapidiff.core;
+
+import io.swagger.v3.oas.models.PathItem.HttpMethod;
+import java.util.Optional;
+import javax.annotation.Nullable;
+import org.openapitools.openapidiff.core.model.ChangedApiResponse;
+import org.openapitools.openapidiff.core.model.ChangedContent;
+import org.openapitools.openapidiff.core.model.ChangedHeaders;
+import org.openapitools.openapidiff.core.model.ChangedMediaType;
+import org.openapitools.openapidiff.core.model.ChangedOpenApi;
+import org.openapitools.openapidiff.core.model.ChangedOperation;
+import org.openapitools.openapidiff.core.model.ChangedParameter;
+import org.openapitools.openapidiff.core.model.ChangedParameters;
+import org.openapitools.openapidiff.core.model.ChangedRequestBody;
+import org.openapitools.openapidiff.core.model.ChangedResponse;
+import org.openapitools.openapidiff.core.model.ChangedSchema;
+
+public class ChangesResolver {
+
+  /**
+   * Get the ChangedOperation for the given method and path.
+   *
+   * @param changedOpenApi the ChangedOpenApi object
+   * @param method the HTTP method
+   * @param path the path
+   * @return the ChangedOperation object
+   */
+  @Nullable
+  public static ChangedOperation getChangedOperation(
+      ChangedOpenApi changedOpenApi, HttpMethod method, String path) {
+    return changedOpenApi.getChangedOperations().stream()
+        .filter(
+            operation ->
+                operation.getHttpMethod().equals(method) && operation.getPathUrl().equals(path))
+        .findFirst()
+        .orElse(null);
+  }
+
+  /**
+   * Get the ChangedParameter for the given method, path, and parameter name.
+   *
+   * @param changedOpenApi the ChangedOpenApi object
+   * @param method the HTTP method
+   * @param path the path
+   * @param parameterName the parameter name
+   * @return the ChangedParameter object
+   */
+  @Nullable
+  public static ChangedParameter getChangedParameter(
+      ChangedOpenApi changedOpenApi, HttpMethod method, String path, String parameterName) {
+    ChangedOperation changedOperation = getChangedOperation(changedOpenApi, method, path);
+
+    if (changedOperation == null) {
+      return null;
+    }
+
+    return Optional.ofNullable(changedOperation.getParameters())
+        .map(ChangedParameters::getChanged)
+        .flatMap(
+            changedParameters ->
+                changedParameters.stream()
+                    .filter(changedParameter -> changedParameter.getName().equals(parameterName))
+                    .findFirst())
+        .orElse(null);
+  }
+
+  /**
+   * Get the ChangedHeaders for the given method, path, and response code.
+   *
+   * @param changedOpenApi the ChangedOpenApi object
+   * @param method the HTTP method
+   * @param path the path
+   * @param responseCode the response code
+   * @return the ChangedHeaders object
+   */
+  @Nullable
+  public static ChangedHeaders getChangedResponseHeaders(
+      ChangedOpenApi changedOpenApi, HttpMethod method, String path, String responseCode) {
+    return Optional.ofNullable(getChangedOperation(changedOpenApi, method, path))
+        .map(ChangedOperation::getApiResponses)
+        .map(ChangedApiResponse::getChanged)
+        .map(responses -> responses.get(responseCode))
+        .map(ChangedResponse::getHeaders)
+        .orElse(null);
+  }
+
+  /**
+   * Get the ChangedSchema for the given method, path, and media type.
+   *
+   * @param changedOpenApi the ChangedOpenApi object
+   * @param method the HTTP method
+   * @param path the path
+   * @param mediaType the media type
+   * @return the ChangedSchema object
+   */
+  @Nullable
+  public static ChangedSchema getRequestBodyChangedSchema(
+      ChangedOpenApi changedOpenApi, HttpMethod method, String path, String mediaType) {
+    ChangedOperation changedOperation = getChangedOperation(changedOpenApi, method, path);
+
+    if (changedOperation == null) {
+      return null;
+    }
+
+    return Optional.ofNullable(changedOperation)
+        .map(ChangedOperation::getRequestBody)
+        .map(ChangedRequestBody::getContent)
+        .map(ChangedContent::getChanged)
+        .map(changedMediaTypes -> changedMediaTypes.get(mediaType))
+        .map(ChangedMediaType::getSchema)
+        .orElse(null);
+  }
+}
diff --git a/core/src/test/java/org/openapitools/openapidiff/core/ConsoleRenderTest.java b/core/src/test/java/org/openapitools/openapidiff/core/ConsoleRenderTest.java
deleted file mode 100644
index 329fea054..000000000
--- a/core/src/test/java/org/openapitools/openapidiff/core/ConsoleRenderTest.java
+++ /dev/null
@@ -1,17 +0,0 @@
-package org.openapitools.openapidiff.core;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-import org.junit.jupiter.api.Test;
-import org.openapitools.openapidiff.core.model.ChangedOpenApi;
-import org.openapitools.openapidiff.core.output.ConsoleRender;
-
-public class ConsoleRenderTest {
-  @Test
-  public void renderDoesNotFailWhenPropertyHasBeenRemoved() {
-    ConsoleRender render = new ConsoleRender();
-    ChangedOpenApi diff =
-        OpenApiCompare.fromLocations("missing_property_1.yaml", "missing_property_2.yaml");
-    assertThat(render.render(diff)).isNotBlank();
-  }
-}
diff --git a/core/src/test/java/org/openapitools/openapidiff/core/ContentDiffTest.java b/core/src/test/java/org/openapitools/openapidiff/core/ContentDiffTest.java
index 190b5a32f..e3cf03534 100644
--- a/core/src/test/java/org/openapitools/openapidiff/core/ContentDiffTest.java
+++ b/core/src/test/java/org/openapitools/openapidiff/core/ContentDiffTest.java
@@ -33,7 +33,7 @@ public void testAddedResponseContentTypeDiff() {
     ChangedOpenApi changedOpenApi =
         OpenApiCompare.fromLocations(
             "content_type_response_add_1.yaml", "content_type_response_add_2.yaml");
-    assertThat(changedOpenApi.isCompatible()).isFalse();
+    assertThat(changedOpenApi.isCompatible()).isTrue();
   }
 
   @Test
@@ -41,7 +41,7 @@ public void testRemovedResponseContentTypeDiff() {
     ChangedOpenApi changedOpenApi =
         OpenApiCompare.fromLocations(
             "content_type_response_add_2.yaml", "content_type_response_add_1.yaml");
-    assertThat(changedOpenApi.isCompatible()).isTrue();
+    assertThat(changedOpenApi.isCompatible()).isFalse();
   }
 
   @Test
diff --git a/core/src/test/java/org/openapitools/openapidiff/core/ExamplesDiffTest.java b/core/src/test/java/org/openapitools/openapidiff/core/ExamplesDiffTest.java
new file mode 100644
index 000000000..1743de4d1
--- /dev/null
+++ b/core/src/test/java/org/openapitools/openapidiff/core/ExamplesDiffTest.java
@@ -0,0 +1,167 @@
+package org.openapitools.openapidiff.core;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.openapitools.openapidiff.core.TestUtils.getOpenApiChangedOperations;
+
+import java.util.List;
+import org.junit.jupiter.api.Test;
+import org.openapitools.openapidiff.core.model.ChangedOperation;
+
+public class ExamplesDiffTest {
+
+  @Test
+  public void issue666OAS30ExampleInMediaType() {
+    List<ChangedOperation> changedOperations =
+        getOpenApiChangedOperations(
+            "issue-666-oas-3-0-examples-handling-1.yaml",
+            "issue-666-oas-3-0-examples-handling-2.yaml");
+
+    ChangedOperation requestMediaTypeExampleChanged =
+        changedOperations.stream()
+            .filter(o -> "/example/request/media-type".equals(o.getPathUrl()))
+            .findFirst()
+            .orElse(null);
+    ChangedOperation responseMediaTypeExampleChanged =
+        changedOperations.stream()
+            .filter(o -> "/example/response/media-type".equals(o.getPathUrl()))
+            .findFirst()
+            .orElse(null);
+
+    assertThat(requestMediaTypeExampleChanged).isNotNull();
+    assertThat(responseMediaTypeExampleChanged).isNotNull();
+  }
+
+  @Test
+  public void issue666OAS31ExamplesInMediaType() {
+    List<ChangedOperation> changedOperations =
+        getOpenApiChangedOperations(
+            "issue-666-oas-3-1-examples-handling-1.yaml",
+            "issue-666-oas-3-1-examples-handling-2.yaml");
+
+    ChangedOperation requestMediaTypeExamplesChanged =
+        changedOperations.stream()
+            .filter(o -> "/examples/request/media-type".equals(o.getPathUrl()))
+            .findFirst()
+            .orElse(null);
+    ChangedOperation responseMediaTypeExamplesChanged =
+        changedOperations.stream()
+            .filter(o -> "/examples/response/media-type".equals(o.getPathUrl()))
+            .findFirst()
+            .orElse(null);
+
+    assertThat(requestMediaTypeExamplesChanged).isNotNull();
+    assertThat(responseMediaTypeExamplesChanged).isNotNull();
+  }
+
+  @Test
+  public void issue666OAS30ExampleInParameter() {
+    List<ChangedOperation> changedOperations =
+        getOpenApiChangedOperations(
+            "issue-666-oas-3-0-examples-handling-1.yaml",
+            "issue-666-oas-3-0-examples-handling-2.yaml");
+
+    ChangedOperation paramExampleChanged =
+        changedOperations.stream()
+            .filter(o -> "/example/parameter/{id}".equals(o.getPathUrl()))
+            .findFirst()
+            .orElse(null);
+
+    assertThat(paramExampleChanged).isNotNull();
+  }
+
+  @Test
+  public void issue666OAS31ExamplesInParameter() {
+    List<ChangedOperation> changedOperations =
+        getOpenApiChangedOperations(
+            "issue-666-oas-3-1-examples-handling-1.yaml",
+            "issue-666-oas-3-1-examples-handling-2.yaml");
+
+    ChangedOperation paramExamplesChanged =
+        changedOperations.stream()
+            .filter(o -> "/examples/parameter/{id}".equals(o.getPathUrl()))
+            .findFirst()
+            .orElse(null);
+
+    assertThat(paramExamplesChanged).isNotNull();
+  }
+
+  @Test
+  public void issue666OAS30ExampleInResponseHeader() {
+    List<ChangedOperation> changedOperations =
+        getOpenApiChangedOperations(
+            "issue-666-oas-3-0-examples-handling-1.yaml",
+            "issue-666-oas-3-0-examples-handling-2.yaml");
+
+    ChangedOperation operation =
+        changedOperations.stream()
+            .filter(o -> "/example/response/header".equals(o.getPathUrl()))
+            .findFirst()
+            .orElse(null);
+
+    assertThat(operation).isNotNull();
+  }
+
+  @Test
+  public void issue666OAS31ExamplesInResponseHeader() {
+    List<ChangedOperation> changedOperations =
+        getOpenApiChangedOperations(
+            "issue-666-oas-3-1-examples-handling-1.yaml",
+            "issue-666-oas-3-1-examples-handling-2.yaml");
+
+    ChangedOperation operation =
+        changedOperations.stream()
+            .filter(o -> "/examples/response/header".equals(o.getPathUrl()))
+            .findFirst()
+            .orElse(null);
+
+    assertThat(operation).isNotNull();
+  }
+
+  @Test
+  public void issue666OAS30ExampleInSchema() {
+    List<ChangedOperation> changedOperations =
+        getOpenApiChangedOperations(
+            "issue-666-oas-3-0-examples-handling-1.yaml",
+            "issue-666-oas-3-0-examples-handling-2.yaml");
+
+    ChangedOperation operation =
+        changedOperations.stream()
+            .filter(o -> "/example/model/property".equals(o.getPathUrl()))
+            .findFirst()
+            .orElse(null);
+
+    assertThat(operation).isNotNull();
+  }
+
+  @Test
+  public void issue666OAS31ExamplesInSchema() {
+    List<ChangedOperation> changedOperations =
+        getOpenApiChangedOperations(
+            "issue-666-oas-3-1-examples-handling-1.yaml",
+            "issue-666-oas-3-1-examples-handling-2.yaml");
+
+    ChangedOperation operation =
+        changedOperations.stream()
+            .filter(o -> "/examples/model/property".equals(o.getPathUrl()))
+            .findFirst()
+            .orElse(null);
+
+    assertThat(operation).isNotNull();
+  }
+
+  @Test
+  public void issue666OAS30ExampleInComposedSchema() {
+    List<ChangedOperation> changedOperations =
+        getOpenApiChangedOperations(
+            "issue-666-oas-3-0-examples-handling-1.yaml",
+            "issue-666-oas-3-0-examples-handling-2.yaml");
+
+    ChangedOperation operation =
+        changedOperations.stream()
+            .filter(o -> "/example/model/property/composed".equals(o.getPathUrl()))
+            .findFirst()
+            .orElse(null);
+
+    assertThat(operation).isNotNull();
+  }
+}
diff --git a/core/src/test/java/org/openapitools/openapidiff/core/HtmlRenderTest.java b/core/src/test/java/org/openapitools/openapidiff/core/HtmlRenderTest.java
deleted file mode 100644
index 6273ef243..000000000
--- a/core/src/test/java/org/openapitools/openapidiff/core/HtmlRenderTest.java
+++ /dev/null
@@ -1,17 +0,0 @@
-package org.openapitools.openapidiff.core;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-import org.junit.jupiter.api.Test;
-import org.openapitools.openapidiff.core.model.ChangedOpenApi;
-import org.openapitools.openapidiff.core.output.HtmlRender;
-
-public class HtmlRenderTest {
-  @Test
-  public void renderDoesNotFailWhenPropertyHasBeenRemoved() {
-    HtmlRender render = new HtmlRender();
-    ChangedOpenApi diff =
-        OpenApiCompare.fromLocations("missing_property_1.yaml", "missing_property_2.yaml");
-    assertThat(render.render(diff)).isNotBlank();
-  }
-}
diff --git a/core/src/test/java/org/openapitools/openapidiff/core/JsonRenderTest.java b/core/src/test/java/org/openapitools/openapidiff/core/JsonRenderTest.java
deleted file mode 100644
index 547ced027..000000000
--- a/core/src/test/java/org/openapitools/openapidiff/core/JsonRenderTest.java
+++ /dev/null
@@ -1,17 +0,0 @@
-package org.openapitools.openapidiff.core;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-import org.junit.jupiter.api.Test;
-import org.openapitools.openapidiff.core.model.ChangedOpenApi;
-import org.openapitools.openapidiff.core.output.JsonRender;
-
-public class JsonRenderTest {
-  @Test
-  public void renderDoesNotFailWhenPropertyHasBeenRemoved() {
-    JsonRender render = new JsonRender();
-    ChangedOpenApi diff =
-        OpenApiCompare.fromLocations("missing_property_1.yaml", "missing_property_2.yaml");
-    assertThat(render.render(diff)).isNotBlank();
-  }
-}
diff --git a/core/src/test/java/org/openapitools/openapidiff/core/MarkdownRenderTest.java b/core/src/test/java/org/openapitools/openapidiff/core/MarkdownRenderTest.java
deleted file mode 100644
index c92a1cd8c..000000000
--- a/core/src/test/java/org/openapitools/openapidiff/core/MarkdownRenderTest.java
+++ /dev/null
@@ -1,17 +0,0 @@
-package org.openapitools.openapidiff.core;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-import org.junit.jupiter.api.Test;
-import org.openapitools.openapidiff.core.model.ChangedOpenApi;
-import org.openapitools.openapidiff.core.output.MarkdownRender;
-
-public class MarkdownRenderTest {
-  @Test
-  public void renderDoesNotFailWhenPropertyHasBeenRemoved() {
-    MarkdownRender render = new MarkdownRender();
-    ChangedOpenApi diff =
-        OpenApiCompare.fromLocations("missing_property_1.yaml", "missing_property_2.yaml");
-    assertThat(render.render(diff)).isNotBlank();
-  }
-}
diff --git a/core/src/test/java/org/openapitools/openapidiff/core/OpenApiDiffTest.java b/core/src/test/java/org/openapitools/openapidiff/core/OpenApiDiffTest.java
index 88ac57b11..ea21bd282 100644
--- a/core/src/test/java/org/openapitools/openapidiff/core/OpenApiDiffTest.java
+++ b/core/src/test/java/org/openapitools/openapidiff/core/OpenApiDiffTest.java
@@ -3,24 +3,30 @@
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.openapitools.openapidiff.core.TestUtils.assertOpenApiAreEquals;
 
-import java.io.FileWriter;
-import java.io.IOException;
-import java.nio.file.Path;
+import io.swagger.parser.OpenAPIParser;
+import io.swagger.v3.oas.models.OpenAPI;
+import io.swagger.v3.parser.core.models.ParseOptions;
+import java.io.ByteArrayOutputStream;
+import java.io.OutputStreamWriter;
 import java.util.List;
 import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.io.TempDir;
 import org.openapitools.openapidiff.core.model.ChangedOpenApi;
 import org.openapitools.openapidiff.core.model.ChangedOperation;
+import org.openapitools.openapidiff.core.model.DiffResult;
 import org.openapitools.openapidiff.core.model.Endpoint;
 import org.openapitools.openapidiff.core.output.HtmlRender;
 import org.openapitools.openapidiff.core.output.JsonRender;
 import org.openapitools.openapidiff.core.output.MarkdownRender;
+import org.openapitools.openapidiff.core.output.Render;
 
 public class OpenApiDiffTest {
 
   private final String OPENAPI_DOC1 = "petstore_v2_1.yaml";
   private final String OPENAPI_DOC2 = "petstore_v2_2.yaml";
   private final String OPENAPI_EMPTY_DOC = "petstore_v2_empty.yaml";
+  private final String OPENAPI_DOC3 = "petstore_openapi3.yaml";
+
+  private static final OpenAPIParser PARSER = new OpenAPIParser();
 
   @Test
   public void testEqual() {
@@ -28,7 +34,7 @@ public void testEqual() {
   }
 
   @Test
-  public void testNewApi(@TempDir Path tempDir) throws IOException {
+  public void testNewApi() {
     ChangedOpenApi changedOpenApi = OpenApiCompare.fromLocations(OPENAPI_EMPTY_DOC, OPENAPI_DOC2);
     List<Endpoint> newEndpoints = changedOpenApi.getNewEndpoints();
     List<Endpoint> missingEndpoints = changedOpenApi.getMissingEndpoints();
@@ -37,18 +43,16 @@ public void testNewApi(@TempDir Path tempDir) throws IOException {
     assertThat(missingEndpoints).isEmpty();
     assertThat(changedEndPoints).isEmpty();
 
-    String html =
-        new HtmlRender("Changelog", "http://deepoove.com/swagger-diff/stylesheets/demo.css")
-            .render(changedOpenApi);
-    final Path path = tempDir.resolve("testNewApi.html");
-    try (FileWriter fw = new FileWriter(path.toFile())) {
-      fw.write(html);
-    }
-    assertThat(path).isNotEmptyFile();
+    ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+    OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream);
+    Render render =
+        new HtmlRender("Changelog", "http://deepoove.com/swagger-diff/stylesheets/demo.css");
+    render.render(changedOpenApi, outputStreamWriter);
+    assertThat(outputStream.toString()).isNotBlank();
   }
 
   @Test
-  public void testDeprecatedApi(@TempDir Path tempDir) throws IOException {
+  public void testDeprecatedApi() {
     ChangedOpenApi changedOpenApi = OpenApiCompare.fromLocations(OPENAPI_DOC1, OPENAPI_EMPTY_DOC);
     List<Endpoint> newEndpoints = changedOpenApi.getNewEndpoints();
     List<Endpoint> missingEndpoints = changedOpenApi.getMissingEndpoints();
@@ -57,51 +61,82 @@ public void testDeprecatedApi(@TempDir Path tempDir) throws IOException {
     assertThat(missingEndpoints).isNotEmpty();
     assertThat(changedEndPoints).isEmpty();
 
-    String html =
-        new HtmlRender("Changelog", "http://deepoove.com/swagger-diff/stylesheets/demo.css")
-            .render(changedOpenApi);
-    final Path path = tempDir.resolve("testDeprecatedApi.html");
-    try (FileWriter fw = new FileWriter(path.toFile())) {
-      fw.write(html);
-    }
-    assertThat(path).isNotEmptyFile();
+    ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+    OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream);
+    Render render =
+        new HtmlRender("Changelog", "http://deepoove.com/swagger-diff/stylesheets/demo.css");
+    render.render(changedOpenApi, outputStreamWriter);
+    assertThat(outputStream.toString()).isNotBlank();
   }
 
   @Test
-  public void testDiff(@TempDir Path tempDir) throws IOException {
+  public void testDiff() {
     ChangedOpenApi changedOpenApi = OpenApiCompare.fromLocations(OPENAPI_DOC1, OPENAPI_DOC2);
     List<ChangedOperation> changedEndPoints = changedOpenApi.getChangedOperations();
     assertThat(changedEndPoints).isNotEmpty();
 
-    String html =
-        new HtmlRender("Changelog", "http://deepoove.com/swagger-diff/stylesheets/demo.css")
-            .render(changedOpenApi);
-    final Path path = tempDir.resolve("testDiff.html");
-    try (FileWriter fw = new FileWriter(path.toFile())) {
-      fw.write(html);
-    }
-    assertThat(path).isNotEmptyFile();
+    ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+    OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream);
+    Render render =
+        new HtmlRender("Changelog", "http://deepoove.com/swagger-diff/stylesheets/demo.css");
+    render.render(changedOpenApi, outputStreamWriter);
+    assertThat(outputStream.toString()).isNotBlank();
   }
 
   @Test
-  public void testDiffAndMarkdown(@TempDir Path tempDir) throws IOException {
+  public void testDiffAndMarkdown() {
     ChangedOpenApi diff = OpenApiCompare.fromLocations(OPENAPI_DOC1, OPENAPI_DOC2);
-    String render = new MarkdownRender().render(diff);
-    final Path path = tempDir.resolve("testDiff.md");
-    try (FileWriter fw = new FileWriter(path.toFile())) {
-      fw.write(render);
-    }
-    assertThat(path).isNotEmptyFile();
+    ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+    OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream);
+    Render render = new MarkdownRender();
+    render.render(diff, outputStreamWriter);
+    assertThat(outputStream.toString()).isNotBlank();
   }
 
   @Test
-  public void testDiffAndJson(@TempDir Path tempDir) throws IOException {
+  public void testDiffAndJson() {
     ChangedOpenApi diff = OpenApiCompare.fromLocations(OPENAPI_DOC1, OPENAPI_DOC2);
-    String render = new JsonRender().render(diff);
-    final Path path = tempDir.resolve("testDiff.json");
-    try (FileWriter fw = new FileWriter(path.toFile())) {
-      fw.write(render);
-    }
-    assertThat(path).isNotEmptyFile();
+    ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+    OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream);
+    Render render = new JsonRender();
+    render.render(diff, outputStreamWriter);
+    assertThat(outputStream.toString()).isNotBlank();
+  }
+
+  /** Testing that repetitive specs comparisons has to produce consistent result. */
+  @Test
+  public void testComparisonConsistency() {
+    final OpenAPI oldSpec = loadSpecFromFile(OPENAPI_DOC3);
+    final OpenAPI newSpec = loadSpecFromFile(OPENAPI_DOC3);
+
+    final ChangedOpenApi diff1 = OpenApiCompare.fromSpecifications(oldSpec, newSpec);
+    assertThat(diff1.isChanged()).isEqualTo(DiffResult.NO_CHANGES);
+    assertThat(diff1.getNewEndpoints()).isEmpty();
+    assertThat(diff1.getMissingEndpoints()).isEmpty();
+    assertThat(diff1.getChangedOperations()).isEmpty();
+
+    final ChangedOpenApi diff2 = OpenApiCompare.fromSpecifications(oldSpec, newSpec);
+    assertThat(diff2.isChanged()).isEqualTo(DiffResult.NO_CHANGES);
+    assertThat(diff2.getNewEndpoints()).isEmpty();
+    assertThat(diff2.getMissingEndpoints()).isEmpty();
+    assertThat(diff2.getChangedOperations()).isEmpty();
+  }
+
+  @Test
+  public void testSpecObjectsAreNotChangesAfterComparison() {
+    final OpenAPI oldSpec = loadSpecFromFile(OPENAPI_DOC3);
+    final OpenAPI newSpec = loadSpecFromFile(OPENAPI_DOC3);
+
+    OpenApiCompare.fromSpecifications(oldSpec, newSpec);
+    OpenApiCompare.fromSpecifications(oldSpec, newSpec);
+
+    final OpenAPI expectedOldSpec = loadSpecFromFile(OPENAPI_DOC3);
+    final OpenAPI expectedNewSpec = loadSpecFromFile(OPENAPI_DOC3);
+    assertThat(oldSpec).isEqualTo(expectedOldSpec);
+    assertThat(newSpec).isEqualTo(expectedNewSpec);
+  }
+
+  private static OpenAPI loadSpecFromFile(String specFile) {
+    return PARSER.readLocation(specFile, null, new ParseOptions()).getOpenAPI();
   }
 }
diff --git a/core/src/test/java/org/openapitools/openapidiff/core/OperationDiffTest.java b/core/src/test/java/org/openapitools/openapidiff/core/OperationDiffTest.java
index 79f929f82..4a1794204 100644
--- a/core/src/test/java/org/openapitools/openapidiff/core/OperationDiffTest.java
+++ b/core/src/test/java/org/openapitools/openapidiff/core/OperationDiffTest.java
@@ -1,23 +1,69 @@
 package org.openapitools.openapidiff.core;
 
+import static io.swagger.v3.oas.models.PathItem.HttpMethod.*;
 import static org.assertj.core.api.Assertions.assertThat;
+import static org.openapitools.openapidiff.core.ChangesResolver.getChangedOperation;
 
 import org.junit.jupiter.api.Test;
 import org.openapitools.openapidiff.core.model.ChangedOpenApi;
+import org.openapitools.openapidiff.core.model.ChangedOperation;
 import org.openapitools.openapidiff.core.model.DiffResult;
 
 public class OperationDiffTest {
 
-  private final String OPENAPI_DOC1 = "operation_diff_1.yaml";
-  private final String OPENAPI_DOC2 = "operation_diff_2.yaml";
+  private final String OPENAPI_DOC1 = "operationDiff/operation_diff_1.yaml";
+  private final String OPENAPI_DOC2 = "operationDiff/operation_diff_2.yaml";
 
   @Test
-  public void testContentDiffWithOneEmptyMediaType() {
+  public void testOperationIdChanged() {
     ChangedOpenApi changedOpenApi = OpenApiCompare.fromLocations(OPENAPI_DOC1, OPENAPI_DOC2);
-    assertThat(changedOpenApi.isChanged()).isEqualTo(DiffResult.METADATA);
-    assertThat(changedOpenApi.isDifferent()).isTrue();
-    assertThat(changedOpenApi.getChangedOperations().size()).isEqualTo(1);
-    assertThat(changedOpenApi.getChangedOperations().get(0).getOperationId().isDifferent())
-        .isTrue();
+    ChangedOperation changedOperation =
+        getChangedOperation(changedOpenApi, GET, "/operation/operation-id");
+
+    assertThat(changedOperation).isNotNull();
+    assertThat(changedOperation.isChanged()).isEqualTo(DiffResult.METADATA);
+    assertThat(changedOperation.getOperationId().getRight()).isEqualTo("changed");
+  }
+
+  @Test
+  public void testOperationSummaryChanged() {
+    ChangedOpenApi changedOpenApi = OpenApiCompare.fromLocations(OPENAPI_DOC1, OPENAPI_DOC2);
+    ChangedOperation changedOperation =
+        getChangedOperation(changedOpenApi, GET, "/operation/summary");
+
+    assertThat(changedOperation).isNotNull();
+    assertThat(changedOperation.isChanged()).isEqualTo(DiffResult.METADATA);
+    assertThat(changedOperation.getSummary().getRight()).isEqualTo("changed");
+  }
+
+  @Test
+  public void testOperationDescriptionChanged() {
+    ChangedOpenApi changedOpenApi = OpenApiCompare.fromLocations(OPENAPI_DOC1, OPENAPI_DOC2);
+    ChangedOperation changedOperation =
+        getChangedOperation(changedOpenApi, GET, "/operation/description");
+
+    assertThat(changedOperation).isNotNull();
+    assertThat(changedOperation.isChanged()).isEqualTo(DiffResult.METADATA);
+    assertThat(changedOperation.getDescription().getRight()).isEqualTo("changed");
+  }
+
+  @Test
+  public void testOperationBecomesDeprecated() {
+    ChangedOpenApi changedOpenApi = OpenApiCompare.fromLocations(OPENAPI_DOC1, OPENAPI_DOC2);
+    ChangedOperation changedOperation =
+        getChangedOperation(changedOpenApi, GET, "/operation/becomes-deprecated");
+
+    assertThat(changedOperation).isNotNull();
+    assertThat(changedOperation.isDeprecated()).isTrue();
+  }
+
+  @Test
+  public void testOperationBecomesNotDeprecated() {
+    ChangedOpenApi changedOpenApi = OpenApiCompare.fromLocations(OPENAPI_DOC1, OPENAPI_DOC2);
+    ChangedOperation changedOperation =
+        getChangedOperation(changedOpenApi, GET, "/operation/becomes-not-deprecated");
+
+    assertThat(changedOperation).isNotNull();
+    assertThat(changedOperation.isDeprecated()).isTrue();
   }
 }
diff --git a/core/src/test/java/org/openapitools/openapidiff/core/ParameterDiffTest.java b/core/src/test/java/org/openapitools/openapidiff/core/ParameterDiffTest.java
index ce233ed8b..40f0cba74 100644
--- a/core/src/test/java/org/openapitools/openapidiff/core/ParameterDiffTest.java
+++ b/core/src/test/java/org/openapitools/openapidiff/core/ParameterDiffTest.java
@@ -1,15 +1,277 @@
 package org.openapitools.openapidiff.core;
 
+import static io.swagger.v3.oas.models.PathItem.HttpMethod.GET;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.openapitools.openapidiff.core.ChangesResolver.getChangedOperation;
+import static org.openapitools.openapidiff.core.ChangesResolver.getChangedParameter;
+import static org.openapitools.openapidiff.core.TestUtils.assertOpenApiAreEquals;
 import static org.openapitools.openapidiff.core.TestUtils.assertOpenApiChangedEndpoints;
 
+import io.swagger.v3.oas.models.parameters.Parameter;
+import java.util.Collections;
+import java.util.List;
+import java.util.Optional;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.DisplayName;
 import org.junit.jupiter.api.Test;
+import org.openapitools.openapidiff.core.model.ChangedOpenApi;
+import org.openapitools.openapidiff.core.model.ChangedOperation;
+import org.openapitools.openapidiff.core.model.ChangedParameter;
+import org.openapitools.openapidiff.core.model.ChangedParameters;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 public class ParameterDiffTest {
-  private final String OPENAPI_DOC1 = "parameters_diff_1.yaml";
-  private final String OPENAPI_DOC2 = "parameters_diff_2.yaml";
+
+  private final String OPENAPI_DOC1 = "parameterDiff/parameter_diff_1.yaml";
+  private final String OPENAPI_DOC2 = "parameterDiff/parameter_diff_2.yaml";
+  private final String OVERLOADED_PARAMETERS = "parameterDiff/parameters_overloading.yaml";
+  private final String DUPLICATED_PARAMETER_TYPES = "parameterDiff/parameters_overloading_2.yaml";
+
+  @Test
+  public void testAddParameter() {
+    ChangedOpenApi changedOpenApi = OpenApiCompare.fromLocations(OPENAPI_DOC1, OPENAPI_DOC2);
+    ChangedOperation changedOperation =
+        getChangedOperation(changedOpenApi, GET, "/parameter/added");
+
+    assertThat(changedOperation).isNotNull();
+    List<Parameter> increasedParams =
+        Optional.ofNullable(changedOperation.getParameters())
+            .map(ChangedParameters::getIncreased)
+            .orElse(Collections.emptyList());
+    assertThat(increasedParams.size()).isEqualTo(1);
+    assertThat(increasedParams.get(0).getName()).isEqualTo("param");
+  }
+
+  @Test
+  public void testRemoveParameter() {
+    ChangedOpenApi changedOpenApi = OpenApiCompare.fromLocations(OPENAPI_DOC1, OPENAPI_DOC2);
+    ChangedOperation changedOperation =
+        getChangedOperation(changedOpenApi, GET, "/parameter/removed");
+
+    assertThat(changedOperation).isNotNull();
+    List<Parameter> missingParams =
+        Optional.ofNullable(changedOperation.getParameters())
+            .map(ChangedParameters::getMissing)
+            .orElse(Collections.emptyList());
+    assertThat(missingParams.size()).isEqualTo(1);
+    assertThat(missingParams.get(0).getName()).isEqualTo("param");
+  }
+
+  @Test
+  public void testParameterBecomesDeprecated() {
+    ChangedOpenApi changedOpenApi = OpenApiCompare.fromLocations(OPENAPI_DOC1, OPENAPI_DOC2);
+    ChangedParameter changedParameter =
+        getChangedParameter(changedOpenApi, GET, "/parameter/becomes-deprecated", "X-Header");
+
+    assertThat(changedParameter).isNotNull();
+    assertThat(changedParameter.isDeprecated()).isTrue();
+  }
+
+  @Test
+  public void testParameterBecomesNotDeprecated() {
+    ChangedOpenApi changedOpenApi = OpenApiCompare.fromLocations(OPENAPI_DOC1, OPENAPI_DOC2);
+    ChangedParameter changedParameter =
+        getChangedParameter(changedOpenApi, GET, "/parameter/becomes-not-deprecated", "X-Header");
+
+    assertThat(changedParameter).isNotNull();
+    assertThat(changedParameter.isDeprecated()).isTrue();
+  }
+
+  @Test
+  public void testParameterRequiredChanged() {
+    ChangedOpenApi changedOpenApi = OpenApiCompare.fromLocations(OPENAPI_DOC1, OPENAPI_DOC2);
+    ChangedParameter changedParameter =
+        getChangedParameter(changedOpenApi, GET, "/parameter/required", "param");
+
+    assertThat(changedParameter).isNotNull();
+    assertThat(changedParameter.isChangeRequired()).isTrue();
+  }
+
+  @Test
+  public void testParameterDescriptionChanged() {
+    ChangedOpenApi changedOpenApi = OpenApiCompare.fromLocations(OPENAPI_DOC1, OPENAPI_DOC2);
+    ChangedParameter changedParameter =
+        getChangedParameter(changedOpenApi, GET, "/parameter/description", "param");
+
+    assertThat(changedParameter).isNotNull();
+    assertThat(changedParameter.getDescription().getRight()).isEqualTo("changed");
+  }
+
+  @Test
+  public void testParameterExplodeChanged() {
+    ChangedOpenApi changedOpenApi = OpenApiCompare.fromLocations(OPENAPI_DOC1, OPENAPI_DOC2);
+    ChangedParameter changedParameter =
+        getChangedParameter(changedOpenApi, GET, "/parameter/explode", "param");
+
+    assertThat(changedParameter).isNotNull();
+    assertThat(changedParameter.isChangeExplode()).isTrue();
+  }
+
+  @Test
+  public void testParameterStyleChanged() {
+    ChangedOpenApi changedOpenApi = OpenApiCompare.fromLocations(OPENAPI_DOC1, OPENAPI_DOC2);
+    ChangedParameter changedParameter =
+        getChangedParameter(changedOpenApi, GET, "/parameter/style", "param");
+
+    assertThat(changedParameter).isNotNull();
+    assertThat(changedParameter.isChangeStyle()).isTrue();
+  }
+
+  @Test
+  void testDiffWithOverloadedParameterTypes() {
+    assertDoesNotThrow(
+        () -> OpenApiCompare.fromLocations(OVERLOADED_PARAMETERS, OVERLOADED_PARAMETERS));
+    assertOpenApiAreEquals(OVERLOADED_PARAMETERS, OVERLOADED_PARAMETERS);
+  }
+
+  @Test
+  void testDiffWithDuplicatedParameterTypes() {
+    assertThrows(
+        IllegalArgumentException.class,
+        () -> OpenApiCompare.fromLocations(DUPLICATED_PARAMETER_TYPES, DUPLICATED_PARAMETER_TYPES),
+        "Two path items have the same signature: /projects/{}");
+  }
+
+  @Test
+  public void issue458MaximumDecreased() {
+    assertOpenApiChangedEndpoints(
+        "parameterDiff/issue-458-integer-limits_1.yaml",
+        "parameterDiff/issue-458-integer-limits_2.yaml");
+  }
+
+  @Test
+  public void issue458MaximumIncreased() {
+    assertOpenApiChangedEndpoints(
+        "parameterDiff/issue-458-integer-limits_1.yaml",
+        "parameterDiff/issue-458-integer-limits_3.yaml");
+  }
+
+  @Test
+  public void issue458MinimumDecreased() {
+    assertOpenApiChangedEndpoints(
+        "parameterDiff/issue-458-integer-limits_1.yaml",
+        "parameterDiff/issue-458-integer-limits_4.yaml");
+  }
+
+  @Test
+  public void issue458MinimumIncreased() {
+    assertOpenApiChangedEndpoints(
+        "parameterDiff/issue-458-integer-limits_1.yaml",
+        "parameterDiff/issue-458-integer-limits_5.yaml");
+  }
+
+  @Test
+  public void issue458IntegerFormatChanged() {
+    assertOpenApiChangedEndpoints(
+        "parameterDiff/issue-458-integer-limits_1.yaml",
+        "parameterDiff/issue-458-integer-limits_6.yaml");
+  }
+
+  @Test
+  public void issue458ExclusiveMinimumChanged() {
+    assertOpenApiChangedEndpoints(
+        "parameterDiff/issue-458-integer-limits_1.yaml",
+        "parameterDiff/issue-458-integer-limits_7.yaml");
+  }
+
+  @Test
+  public void issue458ExclusiveMaximumChanged() {
+    assertOpenApiChangedEndpoints(
+        "parameterDiff/issue-458-integer-limits_1.yaml",
+        "parameterDiff/issue-458-integer-limits_8.yaml");
+  }
+
+  @Test
+  public void issue458ExclusiveMinimumRemoved() {
+    assertOpenApiChangedEndpoints(
+        "parameterDiff/issue-458-integer-limits_1.yaml",
+        "parameterDiff/issue-458-integer-limits_9.yaml");
+  }
+
+  @Test
+  public void issue458ExclusiveMaximumRemoved() {
+    assertOpenApiChangedEndpoints(
+        "parameterDiff/issue-458-integer-limits_1.yaml",
+        "parameterDiff/issue-458-integer-limits_10.yaml");
+  }
+
+  @Test
+  public void issue458ExclusiveMaximumTrueToFalse() {
+    assertOpenApiChangedEndpoints(
+        "parameterDiff/issue-458-integer-limits_11.yaml",
+        "parameterDiff/issue-458-integer-limits_12.yaml");
+  }
+
+  @Test
+  public void issue458ExclusiveMinimumTrueToFalse() {
+    assertOpenApiChangedEndpoints(
+        "parameterDiff/issue-458-integer-limits_11.yaml",
+        "parameterDiff/issue-458-integer-limits_13.yaml");
+  }
+
+  @Test
+  public void issue458ExclusiveMaximumTrueRemoved() {
+    assertOpenApiChangedEndpoints(
+        "parameterDiff/issue-458-integer-limits_11.yaml",
+        "parameterDiff/issue-458-integer-limits_12.yaml");
+  }
+
+  @Test
+  public void issue458ExclusiveMinimumTrueRemoved() {
+    assertOpenApiChangedEndpoints(
+        "parameterDiff/issue-458-integer-limits_11.yaml",
+        "parameterDiff/issue-458-integer-limits_13.yaml");
+  }
+
+  @Test
+  public void issue488RenameParameterAddAndRemoveParameterReturnFalse() {
+    assertOpenApiChangedEndpoints(
+        "parameterDiff/issue-488-1.json", "parameterDiff/issue-488-2.json");
+  }
+
+  final String TEST_MSG_1 =
+      "Testing: \n"
+          + "1. Same path but different pathParameters\n"
+          + "2. different parameters in the parameters: section\n"
+          + "3. Parameters have different schema\n"
+          + "eg:\n"
+          + "old path   -- students/{id}\n"
+          + "old schema -- id: integer\n"
+          + "new path   -- students/{username}\n"
+          + "new schema -- username: string";
+
+  @Test
+  @DisplayName(
+      "Same Path, different PathParams, Params in the `Parameters`: match pathParam, Different Schema")
+  public void pathSamePathParamsDiffParamSameAsInPathButSchemaDiff() {
+    final Logger logger = LoggerFactory.getLogger(ParameterDiffTest.class);
+    logger.info(TEST_MSG_1);
+    String OPENAPI_DOC1 = "parameterDiff/path_parameter_diff_param_schema_diff_old.yaml";
+    String OPENAPI_DOC2 = "parameterDiff/path_parameter_diff_param_schema_diff_new.yaml";
+    ChangedOpenApi diff = OpenApiCompare.fromLocations(OPENAPI_DOC1, OPENAPI_DOC2);
+    Assertions.assertTrue(diff.isDifferent());
+    Assertions.assertFalse(diff.isCompatible());
+  }
+
+  final String TEST_MSG_2 =
+      "Testing: \n"
+          + "1. Same path but different pathParameters\n"
+          + "2. different parameters in the parameters: section\n"
+          + "3. Parameters have same schema\n";
 
   @Test
-  public void testDiffDifferent() {
-    assertOpenApiChangedEndpoints(OPENAPI_DOC1, OPENAPI_DOC2);
+  @DisplayName(
+      "Same Path, different PathParams, Params in the `Parameters`: match pathParam, same Schema")
+  public void pathSamePathParamsDiffParamNameDiffSchemaSame() {
+    final Logger logger = LoggerFactory.getLogger(ParameterDiffTest.class);
+    logger.info(TEST_MSG_2);
+    String OPENAPI_DOC1 = "parameterDiff/path_parameter_diff_param_name_diff_old.yaml";
+    String OPENAPI_DOC2 = "parameterDiff/path_parameter_diff_param_name_diff_new.yaml";
+    ChangedOpenApi diff = OpenApiCompare.fromLocations(OPENAPI_DOC1, OPENAPI_DOC2);
+    Assertions.assertFalse(diff.isDifferent());
+    Assertions.assertTrue(diff.isCompatible());
   }
 }
diff --git a/core/src/test/java/org/openapitools/openapidiff/core/PathDiffTest.java b/core/src/test/java/org/openapitools/openapidiff/core/PathDiffTest.java
index 1c8168c41..858ef520e 100644
--- a/core/src/test/java/org/openapitools/openapidiff/core/PathDiffTest.java
+++ b/core/src/test/java/org/openapitools/openapidiff/core/PathDiffTest.java
@@ -13,6 +13,8 @@ public class PathDiffTest {
   private final String OPENAPI_PATH2 = "path_2.yaml";
   private final String OPENAPI_PATH3 = "path_3.yaml";
   private final String OPENAPI_PATH4 = "path_4.yaml";
+  private final String OPENAPI_PATH5 = "path_5.yaml";
+  private final String OPENAPI_PATH6 = "path_6.yaml";
 
   @Test
   public void testEqual() {
@@ -35,4 +37,13 @@ public void testSameTemplateDifferentMethods() {
                 assertThat(endpoint.getOperation().getOperationId()).isEqualTo("deletePet"));
     assertThat(changedOpenApi.isCompatible()).isTrue();
   }
+
+  @Test
+  public void testDiffWithSimilarBeginningPaths() {
+    ChangedOpenApi changedOpenApi = OpenApiCompare.fromLocations(OPENAPI_PATH5, OPENAPI_PATH6);
+    ChangedOpenApi diff =
+        OpenApiCompare.fromSpecifications(
+            changedOpenApi.getOldSpecOpenApi(), changedOpenApi.getNewSpecOpenApi());
+    assertThat(diff.getChangedOperations()).isEmpty();
+  }
 }
diff --git a/core/src/test/java/org/openapitools/openapidiff/core/ResponseAddedContentSchemaTest.java b/core/src/test/java/org/openapitools/openapidiff/core/ResponseAddedContentSchemaTest.java
index a4f0cabb0..725c7e0e9 100644
--- a/core/src/test/java/org/openapitools/openapidiff/core/ResponseAddedContentSchemaTest.java
+++ b/core/src/test/java/org/openapitools/openapidiff/core/ResponseAddedContentSchemaTest.java
@@ -3,6 +3,8 @@
 import static org.assertj.core.api.Assertions.assertThat;
 
 import io.swagger.v3.oas.models.media.Content;
+import java.io.ByteArrayOutputStream;
+import java.io.OutputStreamWriter;
 import java.util.Map;
 import org.junit.jupiter.api.Test;
 import org.openapitools.openapidiff.core.model.ChangedOpenApi;
@@ -10,6 +12,7 @@
 import org.openapitools.openapidiff.core.output.ConsoleRender;
 import org.openapitools.openapidiff.core.output.HtmlRender;
 import org.openapitools.openapidiff.core.output.MarkdownRender;
+import org.openapitools.openapidiff.core.output.Render;
 
 public class ResponseAddedContentSchemaTest {
 
@@ -39,8 +42,22 @@ public void testDiffDifferent() {
   public void testDiffCanBeRendered() {
     ChangedOpenApi changedOpenApi = OpenApiCompare.fromLocations(OPENAPI_DOC1, OPENAPI_DOC2);
 
-    assertThat(new ConsoleRender().render(changedOpenApi)).isNotBlank();
-    assertThat(new HtmlRender().render(changedOpenApi)).isNotBlank();
-    assertThat(new MarkdownRender().render(changedOpenApi)).isNotBlank();
+    ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+    OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream);
+    Render render = new ConsoleRender();
+    render.render(changedOpenApi, outputStreamWriter);
+    assertThat(outputStream.toString()).isNotBlank();
+
+    outputStream = new ByteArrayOutputStream();
+    outputStreamWriter = new OutputStreamWriter(outputStream);
+    render = new HtmlRender();
+    render.render(changedOpenApi, outputStreamWriter);
+    assertThat(outputStream.toString()).isNotBlank();
+
+    outputStream = new ByteArrayOutputStream();
+    outputStreamWriter = new OutputStreamWriter(outputStream);
+    render = new MarkdownRender();
+    render.render(changedOpenApi, outputStreamWriter);
+    assertThat(outputStream.toString()).isNotBlank();
   }
 }
diff --git a/core/src/test/java/org/openapitools/openapidiff/core/ResponseHeaderDiffTest.java b/core/src/test/java/org/openapitools/openapidiff/core/ResponseHeaderDiffTest.java
index 05e4ecfe3..c388e9692 100644
--- a/core/src/test/java/org/openapitools/openapidiff/core/ResponseHeaderDiffTest.java
+++ b/core/src/test/java/org/openapitools/openapidiff/core/ResponseHeaderDiffTest.java
@@ -1,34 +1,86 @@
 package org.openapitools.openapidiff.core;
 
+import static io.swagger.v3.oas.models.PathItem.HttpMethod.GET;
 import static org.assertj.core.api.Assertions.assertThat;
+import static org.openapitools.openapidiff.core.ChangesResolver.getChangedResponseHeaders;
 
 import java.util.Map;
 import org.junit.jupiter.api.Test;
+import org.openapitools.openapidiff.core.model.ChangedHeader;
 import org.openapitools.openapidiff.core.model.ChangedHeaders;
 import org.openapitools.openapidiff.core.model.ChangedOpenApi;
-import org.openapitools.openapidiff.core.model.ChangedResponse;
 
 public class ResponseHeaderDiffTest {
 
-  private final String OPENAPI_DOC1 = "header_1.yaml";
-  private final String OPENAPI_DOC2 = "header_2.yaml";
+  private final String OPENAPI_DOC1 = "responseHeaderDiff/response_header_1.yaml";
+  private final String OPENAPI_DOC2 = "responseHeaderDiff/response_header_2.yaml";
 
   @Test
-  public void testDiffDifferent() {
+  public void testResponseHeadersDescriptionChanges() {
     ChangedOpenApi changedOpenApi = OpenApiCompare.fromLocations(OPENAPI_DOC1, OPENAPI_DOC2);
+    ChangedHeaders changedResponseHeaders =
+        getChangedResponseHeaders(changedOpenApi, GET, "/response/headers/description", "200");
 
-    assertThat(changedOpenApi.getNewEndpoints()).isEmpty();
-    assertThat(changedOpenApi.getMissingEndpoints()).isEmpty();
-    assertThat(changedOpenApi.getChangedOperations()).isNotEmpty();
-
-    Map<String, ChangedResponse> changedResponses =
-        changedOpenApi.getChangedOperations().get(0).getApiResponses().getChanged();
-    assertThat(changedResponses).isNotEmpty();
-    assertThat(changedResponses).containsKey("200");
-    ChangedHeaders changedHeaders = changedResponses.get("200").getHeaders();
-    assertThat(changedHeaders.isDifferent()).isTrue();
-    assertThat(changedHeaders.getChanged()).hasSize(1);
-    assertThat(changedHeaders.getIncreased()).hasSize(1);
-    assertThat(changedHeaders.getMissing()).hasSize(1);
+    assertThat(changedResponseHeaders).isNotNull();
+    Map<String, ChangedHeader> changedHeaders = changedResponseHeaders.getChanged();
+
+    assertThat(changedHeaders).containsKey("x-header-description-changed");
+    assertThat(changedHeaders.get("x-header-description-changed").getDescription()).isNotNull();
+    assertThat(changedHeaders.get("x-header-description-changed").getDescription().getLeft())
+        .isEqualTo("old description");
+    assertThat(changedHeaders.get("x-header-description-changed").getDescription().getRight())
+        .isEqualTo("new description");
+
+    assertThat(changedHeaders).containsKey("x-header-description-added");
+    assertThat(changedHeaders.get("x-header-description-added").getDescription()).isNotNull();
+    assertThat(changedHeaders.get("x-header-description-added").getDescription().getLeft())
+        .isNull();
+    assertThat(changedHeaders.get("x-header-description-added").getDescription().getRight())
+        .isEqualTo("added description");
+
+    assertThat(changedHeaders).containsKey("x-header-description-removed");
+    assertThat(changedHeaders.get("x-header-description-removed").getDescription()).isNotNull();
+    assertThat(changedHeaders.get("x-header-description-removed").getDescription().getLeft())
+        .isEqualTo("old description");
+    assertThat(changedHeaders.get("x-header-description-removed").getDescription().getRight())
+        .isNull();
+  }
+
+  @Test
+  public void testResponseHeadersRequiredChanges() {
+    ChangedOpenApi changedOpenApi = OpenApiCompare.fromLocations(OPENAPI_DOC1, OPENAPI_DOC2);
+    ChangedHeaders changedResponseHeaders =
+        getChangedResponseHeaders(changedOpenApi, GET, "/response/headers/required", "200");
+
+    assertThat(changedResponseHeaders).isNotNull();
+    Map<String, ChangedHeader> changedHeaders = changedResponseHeaders.getChanged();
+
+    assertThat(changedHeaders).containsKey("x-header-required-changed");
+  }
+
+  @Test
+  public void testResponseHeadersExplodeChanges() {
+    ChangedOpenApi changedOpenApi = OpenApiCompare.fromLocations(OPENAPI_DOC1, OPENAPI_DOC2);
+    ChangedHeaders changedResponseHeaders =
+        getChangedResponseHeaders(changedOpenApi, GET, "/response/headers/explode", "200");
+
+    assertThat(changedResponseHeaders).isNotNull();
+    Map<String, ChangedHeader> changedHeaders = changedResponseHeaders.getChanged();
+
+    assertThat(changedHeaders).containsKey("x-header-explode-changed");
+  }
+
+  @Test // issue #485
+  public void testResponseHeadersDeprecatedChanges() {
+    ChangedOpenApi changedOpenApi = OpenApiCompare.fromLocations(OPENAPI_DOC1, OPENAPI_DOC2);
+    ChangedHeaders changedResponseHeaders =
+        getChangedResponseHeaders(changedOpenApi, GET, "/response/headers/deprecated", "200");
+
+    assertThat(changedResponseHeaders).isNotNull();
+    Map<String, ChangedHeader> changedHeaders = changedResponseHeaders.getChanged();
+    assertThat(changedHeaders).containsKey("x-header-becomes-deprecated");
+    assertThat(changedHeaders.get("x-header-becomes-deprecated").isDeprecated()).isTrue();
+    assertThat(changedHeaders).containsKey("x-header-becomes-not-deprecated");
+    assertThat(changedHeaders.get("x-header-becomes-not-deprecated").isDeprecated()).isTrue();
   }
 }
diff --git a/core/src/test/java/org/openapitools/openapidiff/core/SchemaDiffTest.java b/core/src/test/java/org/openapitools/openapidiff/core/SchemaDiffTest.java
new file mode 100644
index 000000000..17014e868
--- /dev/null
+++ b/core/src/test/java/org/openapitools/openapidiff/core/SchemaDiffTest.java
@@ -0,0 +1,276 @@
+package org.openapitools.openapidiff.core;
+
+import static io.swagger.v3.oas.models.PathItem.HttpMethod.POST;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.openapitools.openapidiff.core.ChangesResolver.getRequestBodyChangedSchema;
+import static org.openapitools.openapidiff.core.TestUtils.assertOpenApiBackwardCompatible;
+
+import java.io.ByteArrayOutputStream;
+import java.io.OutputStreamWriter;
+import java.math.BigDecimal;
+import java.util.Map;
+import org.junit.jupiter.api.Test;
+import org.openapitools.openapidiff.core.model.ChangedOpenApi;
+import org.openapitools.openapidiff.core.model.ChangedSchema;
+import org.openapitools.openapidiff.core.output.ConsoleRender;
+
+public class SchemaDiffTest {
+
+  @Test // issue #717
+  public void schemaPropertyDefaultChanged() {
+    ChangedOpenApi changedOpenApi =
+        OpenApiCompare.fromLocations(
+            "schemaDiff/schema-props-defaults-handling-1.yaml",
+            "schemaDiff/schema-props-defaults-handling-2.yaml");
+
+    assertEquals(1, changedOpenApi.getChangedOperations().size());
+    assertEquals(1, changedOpenApi.getChangedSchemas().size());
+    ChangedSchema changedSchema = changedOpenApi.getChangedSchemas().get(0);
+    assertEquals(1, changedSchema.getChangedProperties().size());
+    assertTrue(changedSchema.getChangedProperties().containsKey("field1"));
+    assertTrue(changedSchema.getChangedProperties().get("field1").isChangeDefault());
+  }
+
+  @Test
+  public void schemaPropertyTypeChanged() {
+    ChangedOpenApi changedOpenApi =
+        OpenApiCompare.fromLocations(
+            "schemaDiff/schema-props-defaults-handling-1.yaml",
+            "schemaDiff/schema-props-defaults-handling-2.yaml");
+  }
+
+  @Test // issue #485
+  public void schemaBecomesDeprecatedTest() {
+    ChangedOpenApi changedOpenApi =
+        OpenApiCompare.fromLocations(
+            "schemaDiff/schema-deprecated-handling-1.yaml",
+            "schemaDiff/schema-deprecated-handling-2.yaml");
+
+    ChangedSchema requestBodySchema =
+        getRequestBodyChangedSchema(
+            changedOpenApi, POST, "/schema-diff/deprecated/added", "application/json");
+    assertNotNull(requestBodySchema);
+    assertTrue(requestBodySchema.isChangeDeprecated());
+  }
+
+  @Test // issue #485
+  public void schemaBecomesNotDeprecatedTest() {
+    ChangedOpenApi changedOpenApi =
+        OpenApiCompare.fromLocations(
+            "schemaDiff/schema-deprecated-handling-1.yaml",
+            "schemaDiff/schema-deprecated-handling-2.yaml");
+
+    ChangedSchema requestBodySchema =
+        getRequestBodyChangedSchema(
+            changedOpenApi, POST, "/schema-diff/deprecated/removed", "application/json");
+    assertNotNull(requestBodySchema);
+    assertTrue(requestBodySchema.isChangeDeprecated());
+  }
+
+  @Test // issue #256
+  void booleanAdditionalPropertiesAreSupported() {
+    ChangedOpenApi diff =
+        OpenApiCompare.fromLocations(
+            "schemaDiff/schema-additional-properties-1.json",
+            "schemaDiff/schema-additional-properties-2.json");
+
+    ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+    OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream);
+    ConsoleRender render = new ConsoleRender();
+    render.render(diff, outputStreamWriter);
+    assertThat(outputStream.toString()).isNotBlank();
+  }
+
+  @Test
+  public void addPropertyHandlingTest() {
+    assertOpenApiBackwardCompatible(
+        "schemaDiff/schema-add-property-1.yaml", "schemaDiff/schema-add-property-2.yaml", true);
+  }
+
+  @Test // issue #537
+  public void addPropertyInPutApiIsCompatible() {
+    ChangedOpenApi changedOpenApi =
+        OpenApiCompare.fromLocations(
+            "schemaDiff/schema-add-property-put-1.yaml",
+            "schemaDiff/schema-add-property-put-2.yaml");
+
+    assertThat(changedOpenApi.isDifferent()).isTrue();
+    assertThat(changedOpenApi.isCompatible()).isTrue();
+  }
+
+  @Test // issues #483 #463
+  public void changeMultipleOfHandling() {
+    ChangedOpenApi changedOpenApi =
+        OpenApiCompare.fromLocations(
+            "schemaDiff/schema-multiple-of-diff-1.yaml",
+            "schemaDiff/schema-multiple-of-diff-2.yaml");
+    ChangedSchema changedSchema =
+        getRequestBodyChangedSchema(
+            changedOpenApi, POST, "/schema/numeric/multiple-of", "application/json");
+
+    assertThat(changedSchema).isNotNull();
+    Map<String, ChangedSchema> props = changedSchema.getChangedProperties();
+    assertThat(props).isNotEmpty();
+
+    // Check changes in multipleOf
+    assertThat(props.get("field1").getMultipleOf().isIncompatible()).isTrue();
+    assertThat(props.get("field1").getMultipleOf().getLeft()).isEqualTo(BigDecimal.valueOf(10));
+    assertThat(props.get("field1").getMultipleOf().getRight()).isEqualTo(BigDecimal.valueOf(20));
+
+    assertThat(props.get("field2").getMultipleOf().isIncompatible()).isTrue();
+    assertThat(props.get("field2").getMultipleOf().getLeft()).isEqualTo(BigDecimal.valueOf(0.01));
+    assertThat(props.get("field2").getMultipleOf().getRight()).isEqualTo(BigDecimal.valueOf(0.1));
+
+    // Check addition of multipleOf
+    assertThat(props.get("field3").getMultipleOf().isIncompatible()).isTrue();
+    assertThat(props.get("field3").getMultipleOf().getLeft()).isNull();
+    assertThat(props.get("field3").getMultipleOf().getRight()).isEqualTo(BigDecimal.valueOf(10));
+
+    // Check deletion of multipleOf
+    assertThat(props.get("field4").getMultipleOf().isCompatible()).isTrue();
+    assertThat(props.get("field4").getMultipleOf().getLeft()).isEqualTo(BigDecimal.valueOf(10));
+    assertThat(props.get("field4").getMultipleOf().getRight()).isNull();
+  }
+
+  @Test // issues #480
+  public void changeMinMaxItemsHandling() {
+    ChangedOpenApi changedOpenApi =
+        OpenApiCompare.fromLocations(
+            "schemaDiff/schema-min-max-items-diff-1.yaml",
+            "schemaDiff/schema-min-max-items-diff-2.yaml");
+    ChangedSchema changedSchema =
+        getRequestBodyChangedSchema(
+            changedOpenApi, POST, "/schema/array/items", "application/json");
+
+    assertThat(changedSchema).isNotNull();
+    Map<String, ChangedSchema> props = changedSchema.getChangedProperties();
+    assertThat(props).isNotEmpty();
+
+    // Check increasing of minItems
+    assertThat(props.get("field1").getMinItems().isIncompatible()).isTrue();
+    assertThat(props.get("field1").getMinItems().getOldValue()).isEqualTo(1);
+    assertThat(props.get("field1").getMinItems().getNewValue()).isEqualTo(2);
+
+    // Check decreasing of minItems
+    assertThat(props.get("field2").getMinItems().isCompatible()).isTrue();
+    assertThat(props.get("field2").getMinItems().getOldValue()).isEqualTo(20);
+    assertThat(props.get("field2").getMinItems().getNewValue()).isEqualTo(10);
+
+    // Check increasing of maxItems
+    assertThat(props.get("field3").getMaxItems().isCompatible()).isTrue();
+    assertThat(props.get("field3").getMaxItems().getOldValue()).isEqualTo(90);
+    assertThat(props.get("field3").getMaxItems().getNewValue()).isEqualTo(100);
+
+    // Check decreasing of maxItems
+    assertThat(props.get("field4").getMaxItems().isIncompatible()).isTrue();
+    assertThat(props.get("field4").getMaxItems().getOldValue()).isEqualTo(100);
+    assertThat(props.get("field4").getMaxItems().getNewValue()).isEqualTo(90);
+  }
+
+  @Test // issue #482
+  public void changeNullableHandling() {
+    ChangedOpenApi changedOpenApi =
+        OpenApiCompare.fromLocations(
+            "schemaDiff/schema-nullable-diff-1.yaml", "schemaDiff/schema-nullable-diff-2.yaml");
+    ChangedSchema changedSchema =
+        getRequestBodyChangedSchema(changedOpenApi, POST, "/schema/nullable", "application/json");
+
+    assertThat(changedSchema).isNotNull();
+    Map<String, ChangedSchema> props = changedSchema.getChangedProperties();
+    assertThat(props).isNotEmpty();
+
+    // Check no changes in nullable
+    assertThat(props.get("field0")).isNull();
+
+    // Check changes in nullable
+    assertThat(props.get("field1").getNullable().isCompatible()).isTrue();
+    assertThat(props.get("field1").getNullable().getLeft()).isTrue();
+    assertThat(props.get("field1").getNullable().getRight()).isFalse();
+
+    // Check deletion of nullable
+    assertThat(props.get("field2").getNullable().isCompatible()).isTrue();
+    assertThat(props.get("field2").getNullable().getLeft()).isTrue();
+    assertThat(props.get("field2").getNullable().getRight()).isNull();
+
+    // Check addition of nullable
+    assertThat(props.get("field3").getNullable().isCompatible()).isTrue();
+    assertThat(props.get("field3").getNullable().getLeft()).isNull();
+    assertThat(props.get("field3").getNullable().getRight()).isTrue();
+  }
+
+  @Test // issue #478
+  public void changeUniqueItemsHandling() {
+    ChangedOpenApi changedOpenApi =
+        OpenApiCompare.fromLocations(
+            "schemaDiff/schema-uniqueItems-diff-1.yaml",
+            "schemaDiff/schema-uniqueItems-diff-2.yaml");
+    ChangedSchema changedSchema =
+        getRequestBodyChangedSchema(
+            changedOpenApi, POST, "/schema/uniqueItems", "application/json");
+
+    assertThat(changedSchema).isNotNull();
+    Map<String, ChangedSchema> props = changedSchema.getChangedProperties();
+    assertThat(props).isNotEmpty();
+
+    // Check no changes in uniqueItems
+    assertThat(props.get("field0")).isNull();
+
+    // Check changes true -> false
+    assertThat(props.get("field1").getUniqueItems().isCompatible()).isTrue();
+    assertThat(props.get("field1").getUniqueItems().getLeft()).isTrue();
+    assertThat(props.get("field1").getUniqueItems().getRight()).isFalse();
+
+    // Check changes false -> true
+    assertThat(props.get("field2").getUniqueItems().isIncompatible()).isTrue();
+    assertThat(props.get("field2").getUniqueItems().getLeft()).isFalse();
+    assertThat(props.get("field2").getUniqueItems().getRight()).isTrue();
+
+    // Check deletion of uniqueItems
+    assertThat(props.get("field3").getUniqueItems().isCompatible()).isTrue();
+    assertThat(props.get("field3").getUniqueItems().getLeft()).isTrue();
+    assertThat(props.get("field3").getUniqueItems().getRight()).isNull();
+
+    // Check addition of uniqueItems
+    assertThat(props.get("field4").getUniqueItems().isIncompatible()).isTrue();
+    assertThat(props.get("field4").getUniqueItems().getLeft()).isNull();
+    assertThat(props.get("field4").getUniqueItems().getRight()).isTrue();
+  }
+
+  @Test // issue #479
+  public void changeMinMaxPropertiesHandling() {
+    ChangedOpenApi changedOpenApi =
+        OpenApiCompare.fromLocations(
+            "schemaDiff/schema-min-max-properties-diff-1.yaml",
+            "schemaDiff/schema-min-max-properties-diff-2.yaml");
+    ChangedSchema changedSchema =
+        getRequestBodyChangedSchema(
+            changedOpenApi, POST, "/schema/object/min-max-properties", "application/json");
+
+    assertThat(changedSchema).isNotNull();
+    Map<String, ChangedSchema> props = changedSchema.getChangedProperties();
+    assertThat(props).isNotEmpty();
+
+    // Check increasing of minProperties
+    assertThat(props.get("field1").getMinProperties().isIncompatible()).isTrue();
+    assertThat(props.get("field1").getMinProperties().getOldValue()).isEqualTo(1);
+    assertThat(props.get("field1").getMinProperties().getNewValue()).isEqualTo(10);
+
+    // Check decreasing of minProperties
+    assertThat(props.get("field2").getMinProperties().isCompatible()).isTrue();
+    assertThat(props.get("field2").getMinProperties().getOldValue()).isEqualTo(10);
+    assertThat(props.get("field2").getMinProperties().getNewValue()).isEqualTo(1);
+
+    // Check increasing of maxProperties
+    assertThat(props.get("field3").getMaxProperties().isCompatible()).isTrue();
+    assertThat(props.get("field3").getMaxProperties().getOldValue()).isEqualTo(10);
+    assertThat(props.get("field3").getMaxProperties().getNewValue()).isEqualTo(100);
+
+    // Check decreasing of maxProperties
+    assertThat(props.get("field4").getMaxProperties().isIncompatible()).isTrue();
+    assertThat(props.get("field4").getMaxProperties().getOldValue()).isEqualTo(100);
+    assertThat(props.get("field4").getMaxProperties().getNewValue()).isEqualTo(10);
+  }
+}
diff --git a/core/src/test/java/org/openapitools/openapidiff/core/TestUtils.java b/core/src/test/java/org/openapitools/openapidiff/core/TestUtils.java
index 1180f2886..597bf830f 100644
--- a/core/src/test/java/org/openapitools/openapidiff/core/TestUtils.java
+++ b/core/src/test/java/org/openapitools/openapidiff/core/TestUtils.java
@@ -3,7 +3,12 @@
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.slf4j.LoggerFactory.getLogger;
 
+import java.util.List;
+import org.openapitools.openapidiff.core.compare.OpenApiDiffOptions;
+import org.openapitools.openapidiff.core.model.BackwardIncompatibleProp;
 import org.openapitools.openapidiff.core.model.ChangedOpenApi;
+import org.openapitools.openapidiff.core.model.ChangedOperation;
+import org.openapitools.openapidiff.core.model.DiffResult;
 import org.slf4j.Logger;
 
 public class TestUtils {
@@ -25,6 +30,38 @@ public static void assertOpenApiChangedEndpoints(String oldSpec, String newSpec)
     assertThat(changedOpenApi.getChangedOperations()).isNotEmpty();
   }
 
+  public static void assertSpecUnchanged(String oldSpec, String newSpec) {
+    ChangedOpenApi changedOpenApi = OpenApiCompare.fromLocations(oldSpec, newSpec);
+    LOG.info("Result: {}", changedOpenApi.isChanged().getValue());
+    assertThat(changedOpenApi.isUnchanged()).isTrue();
+  }
+
+  public static void assertSpecChangedButCompatible(String oldSpec, String newSpec) {
+    ChangedOpenApi changedOpenApi = OpenApiCompare.fromLocations(oldSpec, newSpec);
+    DiffResult diffResult = changedOpenApi.isChanged();
+    LOG.info("Result: {}", diffResult.getValue());
+    assertThat(diffResult.isDifferent()).isTrue();
+    assertThat(diffResult.isCompatible()).isTrue();
+  }
+
+  public static void assertSpecIncompatible(
+      String oldSpec, String newSpec, BackwardIncompatibleProp prop) {
+    OpenApiDiffOptions.Builder builder = OpenApiDiffOptions.builder();
+    // Expect incompatible when BackwardIncompatibleProp enabled
+    builder.configProperty(prop.getPropertyName(), "true");
+    OpenApiDiffOptions optsIncompat = builder.build();
+    ChangedOpenApi apiIncompat = OpenApiCompare.fromLocations(oldSpec, newSpec, null, optsIncompat);
+    LOG.info("Result: {}", apiIncompat.isChanged().getValue());
+    assertThat(apiIncompat.isIncompatible()).isTrue();
+    // Expect changed but compatible when BackwardIncompatibleProp disabled
+    builder.configProperty(prop.getPropertyName(), "false");
+    OpenApiDiffOptions optsCompat = builder.build();
+    ChangedOpenApi apiCompat = OpenApiCompare.fromLocations(oldSpec, newSpec, null, optsCompat);
+    LOG.info("Result: {}", apiCompat.isChanged().getValue());
+    assertThat(apiCompat.isDifferent()).isTrue();
+    assertThat(apiCompat.isCompatible()).isTrue();
+  }
+
   public static void assertOpenApiBackwardCompatible(
       String oldSpec, String newSpec, boolean isDiff) {
     ChangedOpenApi changedOpenApi = OpenApiCompare.fromLocations(oldSpec, newSpec);
@@ -37,4 +74,9 @@ public static void assertOpenApiBackwardIncompatible(String oldSpec, String newS
     LOG.info("Result: {}", changedOpenApi.isChanged().getValue());
     assertThat(changedOpenApi.isIncompatible()).isTrue();
   }
+
+  public static List<ChangedOperation> getOpenApiChangedOperations(String oldSpec, String newSpec) {
+    ChangedOpenApi changedOpenApi = OpenApiCompare.fromLocations(oldSpec, newSpec);
+    return changedOpenApi.getChangedOperations();
+  }
 }
diff --git a/core/src/test/java/org/openapitools/openapidiff/core/backcompat/ApiResponseBCTest.java b/core/src/test/java/org/openapitools/openapidiff/core/backcompat/ApiResponseBCTest.java
new file mode 100644
index 000000000..045a16e48
--- /dev/null
+++ b/core/src/test/java/org/openapitools/openapidiff/core/backcompat/ApiResponseBCTest.java
@@ -0,0 +1,29 @@
+package org.openapitools.openapidiff.core.backcompat;
+
+import static org.openapitools.openapidiff.core.TestUtils.assertSpecChangedButCompatible;
+import static org.openapitools.openapidiff.core.TestUtils.assertSpecIncompatible;
+import static org.openapitools.openapidiff.core.TestUtils.assertSpecUnchanged;
+import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.RESPONSE_RESPONSES_DECREASED;
+
+import org.junit.jupiter.api.Test;
+import org.openapitools.openapidiff.core.model.BackwardIncompatibleProp;
+
+public class ApiResponseBCTest {
+  private final String BASE = "bc_response_apiresponse_base.yaml";
+
+  @Test
+  public void unchanged() {
+    assertSpecUnchanged(BASE, BASE);
+  }
+
+  @Test
+  public void changedButCompatible() {
+    assertSpecChangedButCompatible(BASE, "bc_response_apiresponse_changed_but_compatible.yaml");
+  }
+
+  @Test
+  public void decreased() {
+    BackwardIncompatibleProp prop = RESPONSE_RESPONSES_DECREASED;
+    assertSpecIncompatible(BASE, "bc_response_apiresponse_decreased.yaml", prop);
+  }
+}
diff --git a/core/src/test/java/org/openapitools/openapidiff/core/backcompat/ContentBCTest.java b/core/src/test/java/org/openapitools/openapidiff/core/backcompat/ContentBCTest.java
new file mode 100644
index 000000000..8bd95764e
--- /dev/null
+++ b/core/src/test/java/org/openapitools/openapidiff/core/backcompat/ContentBCTest.java
@@ -0,0 +1,36 @@
+package org.openapitools.openapidiff.core.backcompat;
+
+import static org.openapitools.openapidiff.core.TestUtils.assertSpecChangedButCompatible;
+import static org.openapitools.openapidiff.core.TestUtils.assertSpecIncompatible;
+import static org.openapitools.openapidiff.core.TestUtils.assertSpecUnchanged;
+import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.REQUEST_CONTENT_DECREASED;
+import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.RESPONSE_CONTENT_DECREASED;
+
+import org.junit.jupiter.api.Test;
+import org.openapitools.openapidiff.core.model.BackwardIncompatibleProp;
+
+public class ContentBCTest {
+  private final String BASE = "bc_content_base.yaml";
+
+  @Test
+  public void unchanged() {
+    assertSpecUnchanged(BASE, BASE);
+  }
+
+  @Test
+  public void changedButCompatible() {
+    assertSpecChangedButCompatible(BASE, "bc_content_changed_but_compatible.yaml");
+  }
+
+  @Test
+  public void requestDecreased() {
+    BackwardIncompatibleProp prop = REQUEST_CONTENT_DECREASED;
+    assertSpecIncompatible(BASE, "bc_request_content_decreased.yaml", prop);
+  }
+
+  @Test
+  public void responseDecreased() {
+    BackwardIncompatibleProp prop = RESPONSE_CONTENT_DECREASED;
+    assertSpecIncompatible(BASE, "bc_response_content_decreased.yaml", prop);
+  }
+}
diff --git a/core/src/test/java/org/openapitools/openapidiff/core/backcompat/EnumBCTest.java b/core/src/test/java/org/openapitools/openapidiff/core/backcompat/EnumBCTest.java
new file mode 100644
index 000000000..d5a7921a4
--- /dev/null
+++ b/core/src/test/java/org/openapitools/openapidiff/core/backcompat/EnumBCTest.java
@@ -0,0 +1,36 @@
+package org.openapitools.openapidiff.core.backcompat;
+
+import static org.openapitools.openapidiff.core.TestUtils.assertSpecChangedButCompatible;
+import static org.openapitools.openapidiff.core.TestUtils.assertSpecIncompatible;
+import static org.openapitools.openapidiff.core.TestUtils.assertSpecUnchanged;
+import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.REQUEST_ENUM_DECREASED;
+import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.RESPONSE_ENUM_INCREASED;
+
+import org.junit.jupiter.api.Test;
+import org.openapitools.openapidiff.core.model.BackwardIncompatibleProp;
+
+public class EnumBCTest {
+  private final String BASE = "bc_enum_base.yaml";
+
+  @Test
+  public void unchanged() {
+    assertSpecUnchanged(BASE, BASE);
+  }
+
+  @Test
+  public void changedButCompatible() {
+    assertSpecChangedButCompatible(BASE, "bc_enum_changed_but_compatible.yaml");
+  }
+
+  @Test
+  public void requestDecreased() {
+    BackwardIncompatibleProp prop = REQUEST_ENUM_DECREASED;
+    assertSpecIncompatible(BASE, "bc_request_enum_decreased.yaml", prop);
+  }
+
+  @Test
+  public void responseIncreased() {
+    BackwardIncompatibleProp prop = RESPONSE_ENUM_INCREASED;
+    assertSpecIncompatible(BASE, "bc_response_enum_increased.yaml", prop);
+  }
+}
diff --git a/core/src/test/java/org/openapitools/openapidiff/core/backcompat/HeaderBCTest.java b/core/src/test/java/org/openapitools/openapidiff/core/backcompat/HeaderBCTest.java
new file mode 100644
index 000000000..bf36d7772
--- /dev/null
+++ b/core/src/test/java/org/openapitools/openapidiff/core/backcompat/HeaderBCTest.java
@@ -0,0 +1,44 @@
+package org.openapitools.openapidiff.core.backcompat;
+
+import static org.openapitools.openapidiff.core.TestUtils.assertSpecChangedButCompatible;
+import static org.openapitools.openapidiff.core.TestUtils.assertSpecIncompatible;
+import static org.openapitools.openapidiff.core.TestUtils.assertSpecUnchanged;
+import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.RESPONSE_HEADER_EXPLODE_CHANGED;
+import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.RESPONSE_HEADER_REQUIRED_DECREASED;
+import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.RESPONSE_HEADER_REQUIRED_INCREASED;
+
+import org.junit.jupiter.api.Test;
+import org.openapitools.openapidiff.core.model.BackwardIncompatibleProp;
+
+public class HeaderBCTest {
+  private final String BASE = "bc_response_header_base.yaml";
+
+  @Test
+  public void unchanged() {
+    assertSpecUnchanged(BASE, BASE);
+  }
+
+  @Test
+  public void changedButCompatible() {
+    assertSpecChangedButCompatible(BASE, "bc_response_header_changed_but_compatible.yaml");
+  }
+
+  @Test
+  public void responseExplodeChanged() {
+    BackwardIncompatibleProp prop = RESPONSE_HEADER_EXPLODE_CHANGED;
+    assertSpecIncompatible(BASE, "bc_response_header_explode_changed.yaml", prop);
+  }
+
+  @Test
+  public void responseRequiredDecreased() {
+    BackwardIncompatibleProp prop = RESPONSE_HEADER_REQUIRED_DECREASED;
+    assertSpecIncompatible(BASE, "bc_response_header_required_decreased.yaml", prop);
+  }
+
+  @Test
+  public void responseRequiredIncreased() {
+    // TODO: Document why desired or remove support (test added to avoid unintentional regression)
+    BackwardIncompatibleProp prop = RESPONSE_HEADER_REQUIRED_INCREASED;
+    assertSpecIncompatible(BASE, "bc_response_header_required_increased.yaml", prop);
+  }
+}
diff --git a/core/src/test/java/org/openapitools/openapidiff/core/backcompat/HeadersBCTest.java b/core/src/test/java/org/openapitools/openapidiff/core/backcompat/HeadersBCTest.java
new file mode 100644
index 000000000..e08b4b8c8
--- /dev/null
+++ b/core/src/test/java/org/openapitools/openapidiff/core/backcompat/HeadersBCTest.java
@@ -0,0 +1,29 @@
+package org.openapitools.openapidiff.core.backcompat;
+
+import static org.openapitools.openapidiff.core.TestUtils.assertSpecChangedButCompatible;
+import static org.openapitools.openapidiff.core.TestUtils.assertSpecIncompatible;
+import static org.openapitools.openapidiff.core.TestUtils.assertSpecUnchanged;
+import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.RESPONSE_HEADERS_DECREASED;
+
+import org.junit.jupiter.api.Test;
+import org.openapitools.openapidiff.core.model.BackwardIncompatibleProp;
+
+public class HeadersBCTest {
+  private final String BASE = "bc_response_headers_base.yaml";
+
+  @Test
+  public void unchanged() {
+    assertSpecUnchanged(BASE, BASE);
+  }
+
+  @Test
+  public void changedButCompatible() {
+    assertSpecChangedButCompatible(BASE, "bc_response_headers_changed_but_compatible.yaml");
+  }
+
+  @Test
+  public void responseDecreased() {
+    BackwardIncompatibleProp prop = RESPONSE_HEADERS_DECREASED;
+    assertSpecIncompatible(BASE, "bc_response_headers_decreased.yaml", prop);
+  }
+}
diff --git a/core/src/test/java/org/openapitools/openapidiff/core/backcompat/MaxLengthBCTest.java b/core/src/test/java/org/openapitools/openapidiff/core/backcompat/MaxLengthBCTest.java
new file mode 100644
index 000000000..243ff892a
--- /dev/null
+++ b/core/src/test/java/org/openapitools/openapidiff/core/backcompat/MaxLengthBCTest.java
@@ -0,0 +1,30 @@
+package org.openapitools.openapidiff.core.backcompat;
+
+import static org.openapitools.openapidiff.core.TestUtils.assertSpecIncompatible;
+import static org.openapitools.openapidiff.core.TestUtils.assertSpecUnchanged;
+import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.REQUEST_MAX_LENGTH_DECREASED;
+import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.RESPONSE_MAX_LENGTH_INCREASED;
+
+import org.junit.jupiter.api.Test;
+import org.openapitools.openapidiff.core.model.BackwardIncompatibleProp;
+
+public class MaxLengthBCTest {
+  private final String BASE = "bc_maxlength_base.yaml";
+
+  @Test
+  public void maxLengthUnchanged() {
+    assertSpecUnchanged(BASE, BASE);
+  }
+
+  @Test
+  public void requestMaxLengthDecreased() {
+    BackwardIncompatibleProp prop = REQUEST_MAX_LENGTH_DECREASED;
+    assertSpecIncompatible(BASE, "bc_request_maxlength_decreased.yaml", prop);
+  }
+
+  @Test
+  public void responseMaxLengthIncreased() {
+    BackwardIncompatibleProp prop = RESPONSE_MAX_LENGTH_INCREASED;
+    assertSpecIncompatible(BASE, "bc_response_maxlength_increased.yaml", prop);
+  }
+}
diff --git a/core/src/test/java/org/openapitools/openapidiff/core/backcompat/NumericRangeBCTest.java b/core/src/test/java/org/openapitools/openapidiff/core/backcompat/NumericRangeBCTest.java
new file mode 100644
index 000000000..a589c7ea0
--- /dev/null
+++ b/core/src/test/java/org/openapitools/openapidiff/core/backcompat/NumericRangeBCTest.java
@@ -0,0 +1,111 @@
+package org.openapitools.openapidiff.core.backcompat;
+
+import static org.openapitools.openapidiff.core.TestUtils.assertSpecChangedButCompatible;
+import static org.openapitools.openapidiff.core.TestUtils.assertSpecIncompatible;
+import static org.openapitools.openapidiff.core.TestUtils.assertSpecUnchanged;
+import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.REQUEST_NUMERIC_RANGE_DECREASED;
+import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.RESPONSE_NUMERIC_RANGE_INCREASED;
+
+import org.junit.jupiter.api.Test;
+
+public class NumericRangeBCTest {
+  private final String BASE = "bc_numericrange_base.yaml";
+
+  @Test
+  public void unchanged() {
+    assertSpecUnchanged(BASE, BASE);
+  }
+
+  @Test
+  public void changedButCompatible() {
+    assertSpecChangedButCompatible(BASE, "bc_numericrange_changed_but_compatible.yaml");
+  }
+
+  @Test
+  public void requestExclusiveMaxCreated() {
+    assertIncompatibleRequest("bc_request_numericrange_exclusive_max_created.yaml");
+  }
+
+  @Test
+  public void requestExclusiveMaxSet() {
+    assertIncompatibleRequest("bc_request_numericrange_exclusive_max_set.yaml");
+  }
+
+  @Test
+  public void requestExclusiveMinCreated() {
+    assertIncompatibleRequest("bc_request_numericrange_exclusive_min_created.yaml");
+  }
+
+  @Test
+  public void requestExclusiveMinSet() {
+    assertIncompatibleRequest("bc_request_numericrange_exclusive_min_set.yaml");
+  }
+
+  @Test
+  public void requestMaxAdded() {
+    assertIncompatibleRequest("bc_request_numericrange_max_added.yaml");
+  }
+
+  @Test
+  public void requestMaxDecreased() {
+    assertIncompatibleRequest("bc_request_numericrange_max_decreased.yaml");
+  }
+
+  @Test
+  public void requestMinAdded() {
+    assertIncompatibleRequest("bc_request_numericrange_min_added.yaml");
+  }
+
+  @Test
+  public void requestMinIncreased() {
+    assertIncompatibleRequest("bc_request_numericrange_min_increased.yaml");
+  }
+
+  @Test
+  public void responseExclusiveMaxDeleted() {
+    assertIncompatibleResponse("bc_response_numericrange_exclusive_max_deleted.yaml");
+  }
+
+  @Test
+  public void responseExclusiveMaxUnset() {
+    assertIncompatibleResponse("bc_response_numericrange_exclusive_max_unset.yaml");
+  }
+
+  @Test
+  public void responseExclusiveMinDeleted() {
+    assertIncompatibleResponse("bc_response_numericrange_exclusive_min_deleted.yaml");
+  }
+
+  @Test
+  public void responseExclusiveMinUnset() {
+    assertIncompatibleResponse("bc_response_numericrange_exclusive_min_unset.yaml");
+  }
+
+  @Test
+  public void responseMaxDeleted() {
+    assertIncompatibleResponse("bc_response_numericrange_max_deleted.yaml");
+  }
+
+  @Test
+  public void responseMaxIncreased() {
+    assertIncompatibleResponse("bc_response_numericrange_max_increased.yaml");
+  }
+
+  @Test
+  public void responseMinDecreased() {
+    assertIncompatibleResponse("bc_response_numericrange_min_decreased.yaml");
+  }
+
+  @Test
+  public void responseMinDeleted() {
+    assertIncompatibleResponse("bc_response_numericrange_min_deleted.yaml");
+  }
+
+  private void assertIncompatibleRequest(String newSpec) {
+    assertSpecIncompatible(BASE, newSpec, REQUEST_NUMERIC_RANGE_DECREASED);
+  }
+
+  private void assertIncompatibleResponse(String newSpec) {
+    assertSpecIncompatible(BASE, newSpec, RESPONSE_NUMERIC_RANGE_INCREASED);
+  }
+}
diff --git a/core/src/test/java/org/openapitools/openapidiff/core/backcompat/OAuthFlowBCTest.java b/core/src/test/java/org/openapitools/openapidiff/core/backcompat/OAuthFlowBCTest.java
new file mode 100644
index 000000000..476546504
--- /dev/null
+++ b/core/src/test/java/org/openapitools/openapidiff/core/backcompat/OAuthFlowBCTest.java
@@ -0,0 +1,37 @@
+package org.openapitools.openapidiff.core.backcompat;
+
+import static org.openapitools.openapidiff.core.TestUtils.assertSpecIncompatible;
+import static org.openapitools.openapidiff.core.TestUtils.assertSpecUnchanged;
+import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.SECURITY_SCHEME_OAUTH2_AUTH_URL_CHANGED;
+import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.SECURITY_SCHEME_OAUTH2_REFRESH_URL_CHANGED;
+import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.SECURITY_SCHEME_OAUTH2_TOKEN_URL_CHANGED;
+
+import org.junit.jupiter.api.Test;
+import org.openapitools.openapidiff.core.model.BackwardIncompatibleProp;
+
+public class OAuthFlowBCTest {
+  private final String BASE = "bc_oauthflow_base.yaml";
+
+  @Test
+  public void unchanged() {
+    assertSpecUnchanged(BASE, BASE);
+  }
+
+  @Test
+  public void authorizationUrlChanged() {
+    BackwardIncompatibleProp prop = SECURITY_SCHEME_OAUTH2_AUTH_URL_CHANGED;
+    assertSpecIncompatible(BASE, "bc_oauthflow_authorization_url_changed.yaml", prop);
+  }
+
+  @Test
+  public void refreshUrlChanged() {
+    BackwardIncompatibleProp prop = SECURITY_SCHEME_OAUTH2_REFRESH_URL_CHANGED;
+    assertSpecIncompatible(BASE, "bc_oauthflow_refresh_url_changed.yaml", prop);
+  }
+
+  @Test
+  public void tokenUrlChanged() {
+    BackwardIncompatibleProp prop = SECURITY_SCHEME_OAUTH2_TOKEN_URL_CHANGED;
+    assertSpecIncompatible(BASE, "bc_oauthflow_token_url_changed.yaml", prop);
+  }
+}
diff --git a/core/src/test/java/org/openapitools/openapidiff/core/backcompat/OneOfBCTest.java b/core/src/test/java/org/openapitools/openapidiff/core/backcompat/OneOfBCTest.java
new file mode 100644
index 000000000..fb2c06291
--- /dev/null
+++ b/core/src/test/java/org/openapitools/openapidiff/core/backcompat/OneOfBCTest.java
@@ -0,0 +1,36 @@
+package org.openapitools.openapidiff.core.backcompat;
+
+import static org.openapitools.openapidiff.core.TestUtils.assertSpecChangedButCompatible;
+import static org.openapitools.openapidiff.core.TestUtils.assertSpecIncompatible;
+import static org.openapitools.openapidiff.core.TestUtils.assertSpecUnchanged;
+import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.REQUEST_ONEOF_DECREASED;
+import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.RESPONSE_ONEOF_INCREASED;
+
+import org.junit.jupiter.api.Test;
+import org.openapitools.openapidiff.core.model.BackwardIncompatibleProp;
+
+public class OneOfBCTest {
+  private final String BASE = "bc_oneof_base.yaml";
+
+  @Test
+  public void unchanged() {
+    assertSpecUnchanged(BASE, BASE);
+  }
+
+  @Test
+  public void changedButCompatible() {
+    assertSpecChangedButCompatible(BASE, "bc_oneof_changed_but_compatible.yaml");
+  }
+
+  @Test
+  public void requestDecreased() {
+    BackwardIncompatibleProp prop = REQUEST_ONEOF_DECREASED;
+    assertSpecIncompatible(BASE, "bc_request_oneof_decreased.yaml", prop);
+  }
+
+  @Test
+  public void responseIncreased() {
+    BackwardIncompatibleProp prop = RESPONSE_ONEOF_INCREASED;
+    assertSpecIncompatible(BASE, "bc_response_oneof_increased.yaml", prop);
+  }
+}
diff --git a/core/src/test/java/org/openapitools/openapidiff/core/backcompat/OpenApiBCTest.java b/core/src/test/java/org/openapitools/openapidiff/core/backcompat/OpenApiBCTest.java
new file mode 100644
index 000000000..2d3e53065
--- /dev/null
+++ b/core/src/test/java/org/openapitools/openapidiff/core/backcompat/OpenApiBCTest.java
@@ -0,0 +1,29 @@
+package org.openapitools.openapidiff.core.backcompat;
+
+import static org.openapitools.openapidiff.core.TestUtils.assertSpecChangedButCompatible;
+import static org.openapitools.openapidiff.core.TestUtils.assertSpecIncompatible;
+import static org.openapitools.openapidiff.core.TestUtils.assertSpecUnchanged;
+import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.OPENAPI_ENDPOINTS_DECREASED;
+
+import org.junit.jupiter.api.Test;
+import org.openapitools.openapidiff.core.model.BackwardIncompatibleProp;
+
+public class OpenApiBCTest {
+  private final String BASE = "bc_openapi_base.yaml";
+
+  @Test
+  public void unchanged() {
+    assertSpecUnchanged(BASE, BASE);
+  }
+
+  @Test
+  public void changedButCompatible() {
+    assertSpecChangedButCompatible(BASE, "bc_openapi_changed_but_compatible.yaml");
+  }
+
+  @Test
+  public void endpointsDecreased() {
+    BackwardIncompatibleProp prop = OPENAPI_ENDPOINTS_DECREASED;
+    assertSpecIncompatible(BASE, "bc_openapi_endpoints_decreased.yaml", prop);
+  }
+}
diff --git a/core/src/test/java/org/openapitools/openapidiff/core/backcompat/OperationBCTest.java b/core/src/test/java/org/openapitools/openapidiff/core/backcompat/OperationBCTest.java
new file mode 100644
index 000000000..c95d8c9d2
--- /dev/null
+++ b/core/src/test/java/org/openapitools/openapidiff/core/backcompat/OperationBCTest.java
@@ -0,0 +1,20 @@
+package org.openapitools.openapidiff.core.backcompat;
+
+import static org.openapitools.openapidiff.core.TestUtils.assertSpecChangedButCompatible;
+import static org.openapitools.openapidiff.core.TestUtils.assertSpecUnchanged;
+
+import org.junit.jupiter.api.Test;
+
+public class OperationBCTest {
+  private final String BASE = "bc_operation_base.yaml";
+
+  @Test
+  public void unchanged() {
+    assertSpecUnchanged(BASE, BASE);
+  }
+
+  @Test
+  public void changedButCompatible() {
+    assertSpecChangedButCompatible(BASE, "bc_operation_changed_but_compatible.yaml");
+  }
+}
diff --git a/core/src/test/java/org/openapitools/openapidiff/core/backcompat/ParameterBCTest.java b/core/src/test/java/org/openapitools/openapidiff/core/backcompat/ParameterBCTest.java
new file mode 100644
index 000000000..edcd40073
--- /dev/null
+++ b/core/src/test/java/org/openapitools/openapidiff/core/backcompat/ParameterBCTest.java
@@ -0,0 +1,50 @@
+package org.openapitools.openapidiff.core.backcompat;
+
+import static org.openapitools.openapidiff.core.TestUtils.assertSpecChangedButCompatible;
+import static org.openapitools.openapidiff.core.TestUtils.assertSpecIncompatible;
+import static org.openapitools.openapidiff.core.TestUtils.assertSpecUnchanged;
+import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.REQUEST_PARAMS_REQUIRED_INCREASED;
+import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.REQUEST_PARAM_ALLOWEMPTY_DECREASED;
+import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.REQUEST_PARAM_EXPLODE_CHANGED;
+import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.REQUEST_PARAM_STYLE_CHANGED;
+
+import org.junit.jupiter.api.Test;
+import org.openapitools.openapidiff.core.model.BackwardIncompatibleProp;
+
+public class ParameterBCTest {
+  private final String BASE = "bc_request_param_base.yaml";
+
+  @Test
+  public void unchanged() {
+    assertSpecUnchanged(BASE, BASE);
+  }
+
+  @Test
+  public void changedButCompatible() {
+    assertSpecChangedButCompatible(BASE, "bc_request_param_changed_but_compatible.yaml");
+  }
+
+  @Test
+  public void allowEmptyValueDecreased() {
+    BackwardIncompatibleProp prop = REQUEST_PARAM_ALLOWEMPTY_DECREASED;
+    assertSpecIncompatible(BASE, "bc_request_param_allowemptyvalue_decreased.yaml", prop);
+  }
+
+  @Test
+  public void explodeChanged() {
+    BackwardIncompatibleProp prop = REQUEST_PARAM_EXPLODE_CHANGED;
+    assertSpecIncompatible(BASE, "bc_request_param_explode_changed.yaml", prop);
+  }
+
+  @Test
+  public void requiredIncreased() {
+    BackwardIncompatibleProp prop = REQUEST_PARAMS_REQUIRED_INCREASED;
+    assertSpecIncompatible(BASE, "bc_request_param_required_increased.yaml", prop);
+  }
+
+  @Test
+  public void styleChanged() {
+    BackwardIncompatibleProp prop = REQUEST_PARAM_STYLE_CHANGED;
+    assertSpecIncompatible(BASE, "bc_request_param_style_changed.yaml", prop);
+  }
+}
diff --git a/core/src/test/java/org/openapitools/openapidiff/core/backcompat/ParametersBCTest.java b/core/src/test/java/org/openapitools/openapidiff/core/backcompat/ParametersBCTest.java
new file mode 100644
index 000000000..5f2755bbc
--- /dev/null
+++ b/core/src/test/java/org/openapitools/openapidiff/core/backcompat/ParametersBCTest.java
@@ -0,0 +1,36 @@
+package org.openapitools.openapidiff.core.backcompat;
+
+import static org.openapitools.openapidiff.core.TestUtils.assertSpecChangedButCompatible;
+import static org.openapitools.openapidiff.core.TestUtils.assertSpecIncompatible;
+import static org.openapitools.openapidiff.core.TestUtils.assertSpecUnchanged;
+import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.REQUEST_PARAMS_DECREASED;
+import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.REQUEST_PARAMS_REQUIRED_INCREASED;
+
+import org.junit.jupiter.api.Test;
+import org.openapitools.openapidiff.core.model.BackwardIncompatibleProp;
+
+public class ParametersBCTest {
+  private final String BASE = "bc_request_params_base.yaml";
+
+  @Test
+  public void unchanged() {
+    assertSpecUnchanged(BASE, BASE);
+  }
+
+  @Test
+  public void changedButCompatible() {
+    assertSpecChangedButCompatible(BASE, "bc_request_params_changed_but_compatible.yaml");
+  }
+
+  @Test
+  public void decreased() {
+    BackwardIncompatibleProp prop = REQUEST_PARAMS_DECREASED;
+    assertSpecIncompatible(BASE, "bc_request_params_decreased.yaml", prop);
+  }
+
+  @Test
+  public void requiredIncreased() {
+    BackwardIncompatibleProp prop = REQUEST_PARAMS_REQUIRED_INCREASED;
+    assertSpecIncompatible(BASE, "bc_request_params_required_increased.yaml", prop);
+  }
+}
diff --git a/core/src/test/java/org/openapitools/openapidiff/core/backcompat/PathBCTest.java b/core/src/test/java/org/openapitools/openapidiff/core/backcompat/PathBCTest.java
new file mode 100644
index 000000000..c1b2a60f1
--- /dev/null
+++ b/core/src/test/java/org/openapitools/openapidiff/core/backcompat/PathBCTest.java
@@ -0,0 +1,29 @@
+package org.openapitools.openapidiff.core.backcompat;
+
+import static org.openapitools.openapidiff.core.TestUtils.assertSpecChangedButCompatible;
+import static org.openapitools.openapidiff.core.TestUtils.assertSpecIncompatible;
+import static org.openapitools.openapidiff.core.TestUtils.assertSpecUnchanged;
+import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.OPENAPI_ENDPOINTS_DECREASED;
+
+import org.junit.jupiter.api.Test;
+import org.openapitools.openapidiff.core.model.BackwardIncompatibleProp;
+
+public class PathBCTest {
+  private final String BASE = "bc_path_base.yaml";
+
+  @Test
+  public void unchanged() {
+    assertSpecUnchanged(BASE, BASE);
+  }
+
+  @Test
+  public void changedButCompatible() {
+    assertSpecChangedButCompatible(BASE, "bc_path_changed_but_compatible.yaml");
+  }
+
+  @Test
+  public void opsDecreased() {
+    BackwardIncompatibleProp prop = OPENAPI_ENDPOINTS_DECREASED;
+    assertSpecIncompatible(BASE, "bc_path_ops_decreased.yaml", prop);
+  }
+}
diff --git a/core/src/test/java/org/openapitools/openapidiff/core/backcompat/PathsBCTest.java b/core/src/test/java/org/openapitools/openapidiff/core/backcompat/PathsBCTest.java
new file mode 100644
index 000000000..d865ee44a
--- /dev/null
+++ b/core/src/test/java/org/openapitools/openapidiff/core/backcompat/PathsBCTest.java
@@ -0,0 +1,29 @@
+package org.openapitools.openapidiff.core.backcompat;
+
+import static org.openapitools.openapidiff.core.TestUtils.assertSpecChangedButCompatible;
+import static org.openapitools.openapidiff.core.TestUtils.assertSpecIncompatible;
+import static org.openapitools.openapidiff.core.TestUtils.assertSpecUnchanged;
+import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.OPENAPI_ENDPOINTS_DECREASED;
+
+import org.junit.jupiter.api.Test;
+import org.openapitools.openapidiff.core.model.BackwardIncompatibleProp;
+
+public class PathsBCTest {
+  private final String BASE = "bc_paths_base.yaml";
+
+  @Test
+  public void unchanged() {
+    assertSpecUnchanged(BASE, BASE);
+  }
+
+  @Test
+  public void changedButCompatible() {
+    assertSpecChangedButCompatible(BASE, "bc_paths_changed_but_compatible.yaml");
+  }
+
+  @Test
+  public void decreased() {
+    BackwardIncompatibleProp prop = OPENAPI_ENDPOINTS_DECREASED;
+    assertSpecIncompatible(BASE, "bc_paths_decreased.yaml", prop);
+  }
+}
diff --git a/core/src/test/java/org/openapitools/openapidiff/core/backcompat/ReadOnlyBCTest.java b/core/src/test/java/org/openapitools/openapidiff/core/backcompat/ReadOnlyBCTest.java
new file mode 100644
index 000000000..02d2fa9a0
--- /dev/null
+++ b/core/src/test/java/org/openapitools/openapidiff/core/backcompat/ReadOnlyBCTest.java
@@ -0,0 +1,37 @@
+package org.openapitools.openapidiff.core.backcompat;
+
+import static org.openapitools.openapidiff.core.TestUtils.assertSpecChangedButCompatible;
+import static org.openapitools.openapidiff.core.TestUtils.assertSpecIncompatible;
+import static org.openapitools.openapidiff.core.TestUtils.assertSpecUnchanged;
+import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.REQUEST_READONLY_INCREASED;
+import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.REQUEST_READONLY_REQUIRED_DECREASED;
+
+import org.junit.jupiter.api.Test;
+import org.openapitools.openapidiff.core.model.BackwardIncompatibleProp;
+
+public class ReadOnlyBCTest {
+  private final String BASE = "bc_readonly_base.yaml";
+
+  @Test
+  public void unchanged() {
+    assertSpecUnchanged(BASE, BASE);
+  }
+
+  @Test
+  public void changedButCompatible() {
+    assertSpecChangedButCompatible(BASE, "bc_readonly_changed_but_compatible.yaml");
+  }
+
+  @Test
+  public void requestReadOnlyIncreased() {
+    BackwardIncompatibleProp prop = REQUEST_READONLY_INCREASED;
+    assertSpecIncompatible(BASE, "bc_request_readonly_increased.yaml", prop);
+  }
+
+  @Test
+  public void requestReadOnlyRequiredDecreased() {
+    // Incompatible because a prev RO prop (invalid) is now not RO and required
+    BackwardIncompatibleProp prop = REQUEST_READONLY_REQUIRED_DECREASED;
+    assertSpecIncompatible(BASE, "bc_request_readonly_required_decreased.yaml", prop);
+  }
+}
diff --git a/core/src/test/java/org/openapitools/openapidiff/core/backcompat/RequestBodyBCTest.java b/core/src/test/java/org/openapitools/openapidiff/core/backcompat/RequestBodyBCTest.java
new file mode 100644
index 000000000..5b1000702
--- /dev/null
+++ b/core/src/test/java/org/openapitools/openapidiff/core/backcompat/RequestBodyBCTest.java
@@ -0,0 +1,23 @@
+package org.openapitools.openapidiff.core.backcompat;
+
+import static org.openapitools.openapidiff.core.TestUtils.assertSpecIncompatible;
+import static org.openapitools.openapidiff.core.TestUtils.assertSpecUnchanged;
+import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.OPENAPI_ENDPOINTS_DECREASED;
+
+import org.junit.jupiter.api.Test;
+import org.openapitools.openapidiff.core.model.BackwardIncompatibleProp;
+
+public class RequestBodyBCTest {
+  private final String BASE = "bc_request_body_base.yaml";
+
+  @Test
+  public void unchanged() {
+    assertSpecUnchanged(BASE, BASE);
+  }
+
+  @Test
+  public void requiredChanged() {
+    BackwardIncompatibleProp prop = OPENAPI_ENDPOINTS_DECREASED;
+    assertSpecIncompatible(BASE, "bc_request_body_required_changed.yaml", prop);
+  }
+}
diff --git a/core/src/test/java/org/openapitools/openapidiff/core/backcompat/RequiredBCTest.java b/core/src/test/java/org/openapitools/openapidiff/core/backcompat/RequiredBCTest.java
new file mode 100644
index 000000000..c7cfadb0a
--- /dev/null
+++ b/core/src/test/java/org/openapitools/openapidiff/core/backcompat/RequiredBCTest.java
@@ -0,0 +1,37 @@
+package org.openapitools.openapidiff.core.backcompat;
+
+import static org.openapitools.openapidiff.core.TestUtils.assertSpecChangedButCompatible;
+import static org.openapitools.openapidiff.core.TestUtils.assertSpecIncompatible;
+import static org.openapitools.openapidiff.core.TestUtils.assertSpecUnchanged;
+import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.REQUEST_REQUIRED_INCREASED;
+import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.RESPONSE_REQUIRED_DECREASED;
+
+import org.junit.jupiter.api.Test;
+import org.openapitools.openapidiff.core.model.BackwardIncompatibleProp;
+
+public class RequiredBCTest {
+
+  private final String BASE = "bc_required_base.yaml";
+
+  @Test
+  public void unchanged() {
+    assertSpecUnchanged(BASE, BASE);
+  }
+
+  @Test
+  public void changedButCompatible() {
+    assertSpecChangedButCompatible(BASE, "bc_required_changed_but_compatible.yaml");
+  }
+
+  @Test
+  public void requestRequiredIncreased() {
+    BackwardIncompatibleProp prop = REQUEST_REQUIRED_INCREASED;
+    assertSpecIncompatible(BASE, "bc_request_required_increased.yaml", prop);
+  }
+
+  @Test
+  public void responseRequiredDecreased() {
+    BackwardIncompatibleProp prop = RESPONSE_REQUIRED_DECREASED;
+    assertSpecIncompatible(BASE, "bc_response_required_decreased.yaml", prop);
+  }
+}
diff --git a/core/src/test/java/org/openapitools/openapidiff/core/backcompat/SchemaBCTest.java b/core/src/test/java/org/openapitools/openapidiff/core/backcompat/SchemaBCTest.java
new file mode 100644
index 000000000..781b48f2a
--- /dev/null
+++ b/core/src/test/java/org/openapitools/openapidiff/core/backcompat/SchemaBCTest.java
@@ -0,0 +1,72 @@
+package org.openapitools.openapidiff.core.backcompat;
+
+import static org.openapitools.openapidiff.core.TestUtils.assertOpenApiBackwardIncompatible;
+import static org.openapitools.openapidiff.core.TestUtils.assertSpecChangedButCompatible;
+import static org.openapitools.openapidiff.core.TestUtils.assertSpecIncompatible;
+import static org.openapitools.openapidiff.core.TestUtils.assertSpecUnchanged;
+import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.RESPONSE_REQUIRED_DECREASED;
+import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.SCHEMA_DISCRIMINATOR_CHANGED;
+import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.SCHEMA_TYPE_CHANGED;
+
+import org.junit.jupiter.api.Test;
+import org.openapitools.openapidiff.core.model.BackwardIncompatibleProp;
+
+public class SchemaBCTest {
+  private final String BASE = "bc_schema_base.yaml";
+
+  @Test
+  public void unchanged() {
+    assertSpecUnchanged(BASE, BASE);
+  }
+
+  @Test
+  public void changedButCompatible() {
+    assertSpecChangedButCompatible(BASE, "bc_schema_changed_but_compatible.yaml");
+  }
+
+  @Test
+  public void discriminatorChanged() {
+    BackwardIncompatibleProp prop = SCHEMA_DISCRIMINATOR_CHANGED;
+    assertSpecIncompatible(BASE, "bc_schema_discriminator_changed.yaml", prop);
+  }
+
+  @Test
+  public void requestFormatDecreased() {
+    assertOpenApiBackwardIncompatible(BASE, "bc_request_schema_format_decreased.yaml");
+  }
+
+  @Test
+  public void requestFormatIncreased() {
+    // TODO: Document why desired or remove support (test added to avoid unintentional regression)
+    assertOpenApiBackwardIncompatible(BASE, "bc_request_schema_format_increased.yaml");
+  }
+
+  @Test
+  public void requestPropsPutIncreased() {
+    // See https://github.com/OpenAPITools/openapi-diff/issues/537
+    assertSpecChangedButCompatible(BASE, "bc_request_schema_props_put_increased.yaml");
+  }
+
+  @Test
+  public void responseFormatDecreased() {
+    // TODO: Document why desired or remove support (test added to avoid unintentional regression)
+    assertOpenApiBackwardIncompatible(BASE, "bc_response_schema_format_decreased.yaml");
+  }
+
+  @Test
+  public void responseFormatIncreased() {
+    assertOpenApiBackwardIncompatible(BASE, "bc_response_schema_format_increased.yaml");
+  }
+
+  @Test
+  public void responsePropsRequiredDecreased() {
+    BackwardIncompatibleProp prop = RESPONSE_REQUIRED_DECREASED;
+    assertSpecIncompatible(BASE, "bc_response_schema_props_required_decreased.yaml", prop);
+  }
+
+  @Test
+  public void typeChanged() {
+    BackwardIncompatibleProp prop = SCHEMA_TYPE_CHANGED;
+    assertSpecIncompatible(BASE, "bc_schema_type_changed.yaml", prop);
+  }
+}
diff --git a/core/src/test/java/org/openapitools/openapidiff/core/backcompat/SecurityRequirementBCTest.java b/core/src/test/java/org/openapitools/openapidiff/core/backcompat/SecurityRequirementBCTest.java
new file mode 100644
index 000000000..73a3ca227
--- /dev/null
+++ b/core/src/test/java/org/openapitools/openapidiff/core/backcompat/SecurityRequirementBCTest.java
@@ -0,0 +1,27 @@
+package org.openapitools.openapidiff.core.backcompat;
+
+import static org.openapitools.openapidiff.core.TestUtils.*;
+import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.SECURITY_REQUIREMENT_SCHEMES_INCREASED;
+
+import org.junit.jupiter.api.Test;
+import org.openapitools.openapidiff.core.model.BackwardIncompatibleProp;
+
+public class SecurityRequirementBCTest {
+  private final String BASE = "bc_security_requirement_base.yaml";
+
+  @Test
+  public void unchanged() {
+    assertSpecUnchanged(BASE, BASE);
+  }
+
+  @Test
+  public void changedButCompatible() {
+    assertSpecChangedButCompatible(BASE, "bc_security_requirement_changed_but_compatible.yaml");
+  }
+
+  @Test
+  public void schemesIncreased() {
+    BackwardIncompatibleProp prop = SECURITY_REQUIREMENT_SCHEMES_INCREASED;
+    assertSpecIncompatible(BASE, "bc_security_requirement_schemes_increased.yaml", prop);
+  }
+}
diff --git a/core/src/test/java/org/openapitools/openapidiff/core/backcompat/SecurityRequirementsBCTest.java b/core/src/test/java/org/openapitools/openapidiff/core/backcompat/SecurityRequirementsBCTest.java
new file mode 100644
index 000000000..931b35bce
--- /dev/null
+++ b/core/src/test/java/org/openapitools/openapidiff/core/backcompat/SecurityRequirementsBCTest.java
@@ -0,0 +1,39 @@
+package org.openapitools.openapidiff.core.backcompat;
+
+import static org.openapitools.openapidiff.core.TestUtils.*;
+import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.SECURITY_REQUIREMENTS_DECREASED;
+
+import org.junit.jupiter.api.Test;
+import org.openapitools.openapidiff.core.model.BackwardIncompatibleProp;
+
+public class SecurityRequirementsBCTest {
+  private final String BASE = "bc_security_requirements_base.yaml";
+
+  @Test
+  public void unchanged() {
+    assertSpecUnchanged(BASE, BASE);
+  }
+
+  @Test
+  public void changedButCompatible() {
+    assertSpecChangedButCompatible(BASE, "bc_security_requirements_changed_but_compatible.yaml");
+  }
+
+  // TODO: Dropping *all* security requirements should be compatible. Refactor or document
+  // reasoning. Context: OAS spec is clear that only one of the security requirement objects
+  // need to be satisfied so it makes sense why dropping one could break a client that may
+  // not yet support one of the remaining referenced security schemes. But dropping all
+  // requirements should be compatible.
+  @Test
+  public void decreased() {
+    BackwardIncompatibleProp prop = SECURITY_REQUIREMENTS_DECREASED;
+    assertSpecIncompatible(BASE, "bc_security_requirements_decreased.yaml", prop);
+  }
+
+  // TODO: A missing incompatible check seems to be if requirements increase from zero to 1 or more
+
+  @Test
+  public void schemeTypeChanged() {
+    assertOpenApiBackwardIncompatible(BASE, "bc_security_requirements_scheme_type_changed.yaml");
+  }
+}
diff --git a/core/src/test/java/org/openapitools/openapidiff/core/backcompat/SecuritySchemeBCTest.java b/core/src/test/java/org/openapitools/openapidiff/core/backcompat/SecuritySchemeBCTest.java
new file mode 100644
index 000000000..d89d824d5
--- /dev/null
+++ b/core/src/test/java/org/openapitools/openapidiff/core/backcompat/SecuritySchemeBCTest.java
@@ -0,0 +1,55 @@
+package org.openapitools.openapidiff.core.backcompat;
+
+import static org.openapitools.openapidiff.core.TestUtils.*;
+import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.*;
+
+import org.junit.jupiter.api.Test;
+import org.openapitools.openapidiff.core.model.BackwardIncompatibleProp;
+
+public class SecuritySchemeBCTest {
+  private final String BASE = "bc_security_scheme_base.yaml";
+
+  @Test
+  public void unchanged() {
+    assertSpecUnchanged(BASE, BASE);
+  }
+
+  @Test
+  public void changedButCompatible() {
+    assertSpecChangedButCompatible(BASE, "bc_security_scheme_changed_but_compatible.yaml");
+  }
+
+  @Test
+  public void bearerFormatChanged() {
+    BackwardIncompatibleProp prop = SECURITY_SCHEME_BEARER_FORMAT_CHANGED;
+    assertSpecIncompatible(BASE, "bc_security_scheme_bearer_format_changed.yaml", prop);
+  }
+
+  @Test
+  public void inChanged() {
+    assertOpenApiBackwardIncompatible(BASE, "bc_security_scheme_in_changed.yaml");
+  }
+
+  @Test
+  public void openIdConnectUrlChanged() {
+    BackwardIncompatibleProp prop = SECURITY_SCHEME_OPENIDCONNECT_URL_CHANGED;
+    assertSpecIncompatible(BASE, "bc_security_scheme_open_id_connect_url_changed.yaml", prop);
+  }
+
+  @Test
+  public void schemeChanged() {
+    BackwardIncompatibleProp prop = SECURITY_SCHEME_SCHEME_CHANGED;
+    assertSpecIncompatible(BASE, "bc_security_scheme_scheme_changed.yaml", prop);
+  }
+
+  @Test
+  public void typeChanged() {
+    assertOpenApiBackwardIncompatible(BASE, "bc_security_scheme_type_changed.yaml");
+  }
+
+  @Test
+  public void scopesIncreased() {
+    BackwardIncompatibleProp prop = SECURITY_SCHEME_SCOPES_INCREASED;
+    assertSpecIncompatible(BASE, "bc_security_scheme_scopes_increased.yaml", prop);
+  }
+}
diff --git a/core/src/test/java/org/openapitools/openapidiff/core/backcompat/WriteOnlyBCTest.java b/core/src/test/java/org/openapitools/openapidiff/core/backcompat/WriteOnlyBCTest.java
new file mode 100644
index 000000000..a12853c82
--- /dev/null
+++ b/core/src/test/java/org/openapitools/openapidiff/core/backcompat/WriteOnlyBCTest.java
@@ -0,0 +1,37 @@
+package org.openapitools.openapidiff.core.backcompat;
+
+import static org.openapitools.openapidiff.core.TestUtils.assertSpecChangedButCompatible;
+import static org.openapitools.openapidiff.core.TestUtils.assertSpecIncompatible;
+import static org.openapitools.openapidiff.core.TestUtils.assertSpecUnchanged;
+import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.RESPONSE_WRITEONLY_INCREASED;
+import static org.openapitools.openapidiff.core.model.BackwardIncompatibleProp.RESPONSE_WRITEONLY_REQUIRED_DECREASED;
+
+import org.junit.jupiter.api.Test;
+import org.openapitools.openapidiff.core.model.BackwardIncompatibleProp;
+
+public class WriteOnlyBCTest {
+  private final String BASE = "bc_writeonly_base.yaml";
+
+  @Test
+  public void unchanged() {
+    assertSpecUnchanged(BASE, BASE);
+  }
+
+  @Test
+  public void changedButCompatible() {
+    assertSpecChangedButCompatible(BASE, "bc_writeonly_changed_but_compatible.yaml");
+  }
+
+  @Test
+  public void responseWriteOnlyIncreased() {
+    BackwardIncompatibleProp prop = RESPONSE_WRITEONLY_INCREASED;
+    assertSpecIncompatible(BASE, "bc_response_writeonly_increased.yaml", prop);
+  }
+
+  @Test
+  public void responseWriteOnlyRequiredDecreased() {
+    BackwardIncompatibleProp prop = RESPONSE_WRITEONLY_REQUIRED_DECREASED;
+    // TODO: Document why desired or remove support (test added to avoid unintentional regression)
+    assertSpecIncompatible(BASE, "bc_response_writeonly_required_decreased.yaml", prop);
+  }
+}
diff --git a/core/src/test/java/org/openapitools/openapidiff/core/compare/ApiResponseDiffTest.java b/core/src/test/java/org/openapitools/openapidiff/core/compare/ApiResponseDiffTest.java
deleted file mode 100644
index 97b8ab094..000000000
--- a/core/src/test/java/org/openapitools/openapidiff/core/compare/ApiResponseDiffTest.java
+++ /dev/null
@@ -1,27 +0,0 @@
-package org.openapitools.openapidiff.core.compare;
-
-import static org.openapitools.openapidiff.core.TestUtils.assertOpenApiBackwardCompatible;
-import static org.openapitools.openapidiff.core.TestUtils.assertOpenApiBackwardIncompatible;
-
-import org.junit.jupiter.api.Assertions;
-import org.junit.jupiter.api.Test;
-
-class ApiResponseDiffTest extends Assertions {
-  /**
-   * * This is a regression test - when no responses were set, we would get an exception since the
-   * OpenAPI object has a `null` ApiResponses field.
-   */
-  @Test
-  public void testNoResponseInPrevious() {
-    // The previous API had no response, so adding a response shape is still compatible.
-    assertOpenApiBackwardCompatible(
-        "backwardCompatibility/apiResponse_diff_1.yaml",
-        "backwardCompatibility/apiResponse_diff_2.yaml",
-        true);
-
-    // Removing the response shape is backwards incompatible.
-    assertOpenApiBackwardIncompatible(
-        "backwardCompatibility/apiResponse_diff_2.yaml",
-        "backwardCompatibility/apiResponse_diff_1.yaml");
-  }
-}
diff --git a/core/src/test/java/org/openapitools/openapidiff/core/output/AsciidocRenderTest.java b/core/src/test/java/org/openapitools/openapidiff/core/output/AsciidocRenderTest.java
new file mode 100644
index 000000000..228623a2c
--- /dev/null
+++ b/core/src/test/java/org/openapitools/openapidiff/core/output/AsciidocRenderTest.java
@@ -0,0 +1,96 @@
+package org.openapitools.openapidiff.core.output;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.io.ByteArrayOutputStream;
+import java.io.OutputStreamWriter;
+import org.junit.jupiter.api.Test;
+import org.openapitools.openapidiff.core.OpenApiCompare;
+import org.openapitools.openapidiff.core.model.ChangedOpenApi;
+
+public class AsciidocRenderTest {
+  @Test
+  public void renderDoesNotFailWhenPropertyHasBeenRemoved() {
+    AsciidocRender render = new AsciidocRender();
+    ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+    OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream);
+    ChangedOpenApi diff =
+        OpenApiCompare.fromLocations("missing_property_1.yaml", "missing_property_2.yaml");
+    render.render(diff, outputStreamWriter);
+    assertThat(outputStream.toString()).isNotBlank();
+  }
+
+  @Test
+  public void renderDoesNotCauseStackOverflowWithRecursiveDefinitions() {
+    AsciidocRender render = new AsciidocRender();
+    ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+    OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream);
+    ChangedOpenApi diff = OpenApiCompare.fromLocations("recursive_old.yaml", "recursive_new.yaml");
+    render.render(diff, outputStreamWriter);
+    assertThat(outputStream.toString()).isNotBlank();
+  }
+
+  @Test
+  public void renderDoesNotFailWhenHTTPStatusCodeIsRange() {
+    AsciidocRender render = new AsciidocRender();
+    ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+    OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream);
+    ChangedOpenApi diff =
+        OpenApiCompare.fromLocations("range_statuscode_1.yaml", "range_statuscode_2.yaml");
+    render.render(diff, outputStreamWriter);
+    assertThat(outputStream.toString()).isNotBlank();
+  }
+
+  @Test
+  public void validateAsciiDocChangeFile() {
+    AsciidocRender render = new AsciidocRender();
+    ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+    OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream);
+    ChangedOpenApi diff =
+        OpenApiCompare.fromLocations("missing_property_1.yaml", "missing_property_2.yaml");
+    render.render(diff, outputStreamWriter);
+    assertThat(outputStream.toString())
+        .isEqualTo(
+            "= TITLE (v 1.0.0)\n"
+                + ":reproducible:\n"
+                + ":sectlinks:\n"
+                + ":toc:\n"
+                + "\n"
+                + "== What's Changed\n"
+                + "=== GET   /\n"
+                + "* Return Type:\n"
+                + "** Changed default \n"
+                + "** Media types:\n"
+                + "*** Changed application/json\n"
+                + "*** Schema:\n"
+                + "Backward compatible\n"
+                + "\n"
+                + "\n"
+                + "NOTE: API changes are backward compatible\n");
+  }
+
+  @Test
+  public void validateAsciiDocRangeStatus() {
+    AsciidocRender render = new AsciidocRender();
+    ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+    OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream);
+    ChangedOpenApi diff =
+        OpenApiCompare.fromLocations("range_statuscode_1.yaml", "range_statuscode_2.yaml");
+    render.render(diff, outputStreamWriter);
+    assertThat(outputStream.toString())
+        .isEqualTo(
+            "= PROJECTS API (v 1.0.0)\n"
+                + ":reproducible:\n"
+                + ":sectlinks:\n"
+                + ":toc:\n"
+                + "\n"
+                + "== What's Changed\n"
+                + "=== GET   /pet/\n"
+                + "* Return Type:\n"
+                + "** Add 4XX \n"
+                + "** Deleted 405 Method Not Allowed\n"
+                + "\n"
+                + "\n"
+                + "WARNING: API changes broke backward compatibility\n");
+  }
+}
diff --git a/core/src/test/java/org/openapitools/openapidiff/core/output/ConsoleRenderTest.java b/core/src/test/java/org/openapitools/openapidiff/core/output/ConsoleRenderTest.java
new file mode 100644
index 000000000..675633fcd
--- /dev/null
+++ b/core/src/test/java/org/openapitools/openapidiff/core/output/ConsoleRenderTest.java
@@ -0,0 +1,79 @@
+package org.openapitools.openapidiff.core.output;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.io.ByteArrayOutputStream;
+import java.io.OutputStreamWriter;
+import org.junit.jupiter.api.Test;
+import org.openapitools.openapidiff.core.OpenApiCompare;
+import org.openapitools.openapidiff.core.model.ChangedOpenApi;
+
+public class ConsoleRenderTest {
+  @Test
+  public void renderDoesNotFailWhenPropertyHasBeenRemoved() {
+    ConsoleRender render = new ConsoleRender();
+    ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+    OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream);
+    ChangedOpenApi diff =
+        OpenApiCompare.fromLocations("missing_property_1.yaml", "missing_property_2.yaml");
+    render.render(diff, outputStreamWriter);
+    assertThat(outputStream.toString()).isNotBlank();
+  }
+
+  @Test
+  public void renderDoesNotFailWhenHTTPStatusCodeIsRange() {
+    ConsoleRender render = new ConsoleRender();
+    ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+    OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream);
+    ChangedOpenApi diff =
+        OpenApiCompare.fromLocations("range_statuscode_1.yaml", "range_statuscode_2.yaml");
+    render.render(diff, outputStreamWriter);
+    assertThat(outputStream.toString()).isNotBlank();
+  }
+
+  @Test
+  public void renderShowsWhatsDeletedSectionWhenEndpointIsDeleted() {
+    ConsoleRender render = new ConsoleRender();
+    ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+    OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream);
+    ChangedOpenApi diff =
+        OpenApiCompare.fromLocations("delete_endpoint_1.yaml", "delete_endpoint_2.yaml");
+    render.render(diff, outputStreamWriter);
+    assertThat(outputStream.toString()).contains("What's Deleted");
+  }
+
+  @Test
+  public void renderShowsWhatsNewSectionWhenEndpointIsAdded() {
+    ConsoleRender render = new ConsoleRender();
+    ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+    OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream);
+    ChangedOpenApi diff =
+        OpenApiCompare.fromLocations("add_endpoint_1.yaml", "add_endpoint_2.yaml");
+    render.render(diff, outputStreamWriter);
+    assertThat(outputStream.toString()).contains("What's New");
+  }
+
+  @Test
+  public void renderShowsWhatsDeprecatedSectionWhenEndpointIsDeprecated() {
+    ConsoleRender render = new ConsoleRender();
+    ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+    OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream);
+    ChangedOpenApi diff =
+        OpenApiCompare.fromLocations("deprecate_endpoint_1.yaml", "deprecate_endpoint_2.yaml");
+    render.render(diff, outputStreamWriter);
+    assertThat(outputStream.toString()).contains("What's Deprecated");
+  }
+
+  @Test
+  public void renderShowsWhatsChangedSectionWithCorrectFormattingWhenEndpointIsChanged() {
+    ConsoleRender render = new ConsoleRender();
+    ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+    OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream);
+    ChangedOpenApi diff =
+        OpenApiCompare.fromLocations("change_endpoint_1.yaml", "change_endpoint_2.yaml");
+    render.render(diff, outputStreamWriter);
+    assertThat(outputStream.toString())
+        .contains("What's Changed")
+        .containsSubsequence("- GET    /widgets", "Parameter:", "- Changed query-param-1 in query");
+  }
+}
diff --git a/core/src/test/java/org/openapitools/openapidiff/core/output/HtmlRenderTest.java b/core/src/test/java/org/openapitools/openapidiff/core/output/HtmlRenderTest.java
new file mode 100644
index 000000000..e62268d10
--- /dev/null
+++ b/core/src/test/java/org/openapitools/openapidiff/core/output/HtmlRenderTest.java
@@ -0,0 +1,22 @@
+package org.openapitools.openapidiff.core.output;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.io.ByteArrayOutputStream;
+import java.io.OutputStreamWriter;
+import org.junit.jupiter.api.Test;
+import org.openapitools.openapidiff.core.OpenApiCompare;
+import org.openapitools.openapidiff.core.model.ChangedOpenApi;
+
+public class HtmlRenderTest {
+  @Test
+  public void renderDoesNotFailWhenPropertyHasBeenRemoved() {
+    HtmlRender render = new HtmlRender();
+    ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+    OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream);
+    ChangedOpenApi diff =
+        OpenApiCompare.fromLocations("missing_property_1.yaml", "missing_property_2.yaml");
+    render.render(diff, outputStreamWriter);
+    assertThat(outputStream.toString()).isNotBlank();
+  }
+}
diff --git a/core/src/test/java/org/openapitools/openapidiff/core/output/JsonRenderTest.java b/core/src/test/java/org/openapitools/openapidiff/core/output/JsonRenderTest.java
new file mode 100644
index 000000000..da5ce9ecc
--- /dev/null
+++ b/core/src/test/java/org/openapitools/openapidiff/core/output/JsonRenderTest.java
@@ -0,0 +1,33 @@
+package org.openapitools.openapidiff.core.output;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.io.ByteArrayOutputStream;
+import java.io.OutputStreamWriter;
+import org.junit.jupiter.api.Test;
+import org.openapitools.openapidiff.core.OpenApiCompare;
+import org.openapitools.openapidiff.core.model.ChangedOpenApi;
+
+public class JsonRenderTest {
+  @Test
+  public void renderDoesNotFailWhenPropertyHasBeenRemoved() {
+    JsonRender render = new JsonRender();
+    ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+    OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream);
+    ChangedOpenApi diff =
+        OpenApiCompare.fromLocations("missing_property_1.yaml", "missing_property_2.yaml");
+    render.render(diff, outputStreamWriter);
+    assertThat(outputStream.toString()).isNotBlank();
+  }
+
+  @Test
+  public void renderDoesNotFailForJsr310Types() {
+    JsonRender render = new JsonRender();
+    ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+    OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream);
+    ChangedOpenApi diff =
+        OpenApiCompare.fromLocations("jsr310_property_1.yaml", "jsr310_property_2.yaml");
+    render.render(diff, outputStreamWriter);
+    assertThat(outputStream.toString()).isNotBlank();
+  }
+}
diff --git a/core/src/test/java/org/openapitools/openapidiff/core/output/MarkdownRenderTest.java b/core/src/test/java/org/openapitools/openapidiff/core/output/MarkdownRenderTest.java
new file mode 100644
index 000000000..fb1f24fef
--- /dev/null
+++ b/core/src/test/java/org/openapitools/openapidiff/core/output/MarkdownRenderTest.java
@@ -0,0 +1,43 @@
+package org.openapitools.openapidiff.core.output;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.io.ByteArrayOutputStream;
+import java.io.OutputStreamWriter;
+import org.junit.jupiter.api.Test;
+import org.openapitools.openapidiff.core.OpenApiCompare;
+import org.openapitools.openapidiff.core.model.ChangedOpenApi;
+
+public class MarkdownRenderTest {
+  @Test
+  public void renderDoesNotFailWhenPropertyHasBeenRemoved() {
+    MarkdownRender render = new MarkdownRender();
+    ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+    OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream);
+    ChangedOpenApi diff =
+        OpenApiCompare.fromLocations("missing_property_1.yaml", "missing_property_2.yaml");
+    render.render(diff, outputStreamWriter);
+    assertThat(outputStream.toString()).isNotBlank();
+  }
+
+  @Test
+  public void renderDoesNotCauseStackOverflowWithRecursiveDefinitions() {
+    MarkdownRender render = new MarkdownRender();
+    ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+    OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream);
+    ChangedOpenApi diff = OpenApiCompare.fromLocations("recursive_old.yaml", "recursive_new.yaml");
+    render.render(diff, outputStreamWriter);
+    assertThat(outputStream.toString()).isNotBlank();
+  }
+
+  @Test
+  public void renderDoesNotFailWhenHTTPStatusCodeIsRange() {
+    MarkdownRender render = new MarkdownRender();
+    ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+    OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream);
+    ChangedOpenApi diff =
+        OpenApiCompare.fromLocations("range_statuscode_1.yaml", "range_statuscode_2.yaml");
+    render.render(diff, outputStreamWriter);
+    assertThat(outputStream.toString()).isNotBlank();
+  }
+}
diff --git a/core/src/test/java/org/openapitools/openapidiff/core/output/RenderTest.java b/core/src/test/java/org/openapitools/openapidiff/core/output/RenderTest.java
new file mode 100644
index 000000000..86f1e5842
--- /dev/null
+++ b/core/src/test/java/org/openapitools/openapidiff/core/output/RenderTest.java
@@ -0,0 +1,27 @@
+package org.openapitools.openapidiff.core.output;
+
+import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
+
+import java.io.IOException;
+import org.junit.jupiter.api.Test;
+import org.openapitools.openapidiff.core.exception.RendererException;
+import org.openapitools.openapidiff.core.model.ChangedOpenApi;
+
+class RenderTest {
+
+  private final Render testRenderImpl =
+      (diff, outputStreamWriter) -> {
+        try {
+          outputStreamWriter.write("Output");
+          outputStreamWriter.close();
+        } catch (IOException e) {
+          throw new RendererException(e);
+        }
+      };
+
+  @Test
+  void testDefaultRenderMethod() {
+    ChangedOpenApi diff = new ChangedOpenApi(null);
+    assertThat(testRenderImpl.render(diff)).isEqualTo("Output");
+  }
+}
diff --git a/core/src/test/java/org/openapitools/openapidiff/core/utils/FileUtilsTest.java b/core/src/test/java/org/openapitools/openapidiff/core/utils/FileUtilsTest.java
new file mode 100644
index 000000000..73c1e53b0
--- /dev/null
+++ b/core/src/test/java/org/openapitools/openapidiff/core/utils/FileUtilsTest.java
@@ -0,0 +1,59 @@
+package org.openapitools.openapidiff.core.utils;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Collections;
+import org.apache.commons.lang3.StringUtils;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.io.TempDir;
+import org.openapitools.openapidiff.core.compare.OpenApiDiffOptions;
+import org.openapitools.openapidiff.core.model.ChangedOpenApi;
+import org.openapitools.openapidiff.core.output.ConsoleRender;
+
+class FileUtilsTest {
+  private ChangedOpenApi changedOpenApi;
+
+  @BeforeEach
+  void setup() {
+    changedOpenApi = new ChangedOpenApi(OpenApiDiffOptions.builder().build());
+    changedOpenApi.setChangedSchemas(Collections.emptyList());
+    changedOpenApi.setChangedOperations(Collections.emptyList());
+    changedOpenApi.setNewEndpoints(Collections.emptyList());
+    changedOpenApi.setMissingEndpoints(Collections.emptyList());
+  }
+
+  @Test
+  void writeToFile_filenameIsNull_doesNothing() {
+    assertDoesNotThrow(() -> FileUtils.writeToFile(new ConsoleRender(), changedOpenApi, null));
+  }
+
+  @Test
+  void writeToFile_filenameIsEmpty_doesNothing() {
+    assertDoesNotThrow(
+        () -> FileUtils.writeToFile(new ConsoleRender(), changedOpenApi, StringUtils.EMPTY));
+  }
+
+  @Test
+  void writeToFile_fileExists_overwrites_file(@TempDir Path tempDir) throws IOException {
+    final Path path = tempDir.resolve("output.txt");
+    Files.write(path, "Test".getBytes(StandardCharsets.UTF_8));
+
+    assertDoesNotThrow(
+        () -> FileUtils.writeToFile(new ConsoleRender(), changedOpenApi, path.toString()));
+    assertThat(path).exists().content().isNotEqualTo("Test");
+  }
+
+  @Test
+  void writeToFile_fileDoesNotExist_createsFile(@TempDir Path tempDir) {
+    final Path path = tempDir.resolve("output.txt");
+    assertDoesNotThrow(
+        () -> FileUtils.writeToFile(new ConsoleRender(), changedOpenApi, path.toString()));
+    assertThat(path).exists().content().isNotBlank();
+  }
+}
diff --git a/core/src/test/resources/add_endpoint_1.yaml b/core/src/test/resources/add_endpoint_1.yaml
new file mode 100644
index 000000000..0c544c7b4
--- /dev/null
+++ b/core/src/test/resources/add_endpoint_1.yaml
@@ -0,0 +1,35 @@
+openapi: 3.0.0
+servers:
+  - url: 'http://petstore.swagger.io/v2'
+info:
+  description: >-
+    This is a sample server Petstore server.  You can find out more about
+    Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net,
+    #swagger](http://swagger.io/irc/).  For this sample, you can use the api key
+    `special-key` to test the authorization filters.
+  version: 1.0.0
+  title: Swagger Petstore
+  termsOfService: 'http://swagger.io/terms/'
+  contact:
+    email: apiteam@swagger.io
+  license:
+    name: Apache 2.0
+    url: 'http://www.apache.org/licenses/LICENSE-2.0.html'
+paths:
+  /pet/{petId}:
+    get:
+      tags:
+        - pet
+      summary: gets a pet by id
+      description: ''
+      operationId: updatePetWithForm
+      parameters:
+        - name: petId
+          in: path
+          description: ID of pet that needs to be updated
+          required: true
+          schema:
+            type: integer
+      responses:
+        '405':
+          description: Invalid input
diff --git a/core/src/test/resources/add_endpoint_2.yaml b/core/src/test/resources/add_endpoint_2.yaml
new file mode 100644
index 000000000..95906047e
--- /dev/null
+++ b/core/src/test/resources/add_endpoint_2.yaml
@@ -0,0 +1,53 @@
+openapi: 3.0.0
+servers:
+  - url: 'http://petstore.swagger.io/v2'
+info:
+  description: >-
+    This is a sample server Petstore server.  You can find out more about
+    Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net,
+    #swagger](http://swagger.io/irc/).  For this sample, you can use the api key
+    `special-key` to test the authorization filters.
+  version: 1.0.0
+  title: Swagger Petstore
+  termsOfService: 'http://swagger.io/terms/'
+  contact:
+    email: apiteam@swagger.io
+  license:
+    name: Apache 2.0
+    url: 'http://www.apache.org/licenses/LICENSE-2.0.html'
+paths:
+  /pet/{petId}:
+    get:
+      tags:
+        - pet
+      summary: gets a pet by id
+      description: ''
+      operationId: updatePetWithForm
+      parameters:
+        - name: petId
+          in: path
+          description: ID of pet that needs to be updated
+          required: true
+          schema:
+            type: integer
+      responses:
+        '405':
+          description: Invalid input
+  /pet/{petId2}:
+    post:
+      tags:
+        - pet
+      summary: deletes a pet
+      description: ''
+      operationId: deletePet
+      deprecated: true
+      parameters:
+        - name: petId2
+          in: path
+          description: Pet ID to delete
+          required: true
+          schema:
+            type: integer
+      responses:
+        '405':
+          description: Invalid input
diff --git a/core/src/test/resources/backwardCompatibility/apiResponse_diff_1.yaml b/core/src/test/resources/backwardCompatibility/apiResponse_diff_1.yaml
deleted file mode 100644
index fb1e6047d..000000000
--- a/core/src/test/resources/backwardCompatibility/apiResponse_diff_1.yaml
+++ /dev/null
@@ -1,7 +0,0 @@
-openapi: 3.0.0
-info:
-  title: Swagger Petstore
-paths:
-  /store/inventory:
-    get:
-      operationId: asdf
\ No newline at end of file
diff --git a/core/src/test/resources/backwardCompatibility/apiResponse_diff_2.yaml b/core/src/test/resources/backwardCompatibility/apiResponse_diff_2.yaml
deleted file mode 100644
index 82da7cfd6..000000000
--- a/core/src/test/resources/backwardCompatibility/apiResponse_diff_2.yaml
+++ /dev/null
@@ -1,15 +0,0 @@
-openapi: 3.0.0
-info:
-  title: Swagger Petstore
-paths:
-  /store/inventory:
-    get:
-      responses:
-        '200':
-          content:
-            application/json:
-              schema:
-                type: object
-                properties:
-                  title:
-                    type: string
\ No newline at end of file
diff --git a/core/src/test/resources/backwardCompatibility/bc_1.yaml b/core/src/test/resources/backwardCompatibility/bc_1.yaml
deleted file mode 100644
index 8247d2fcb..000000000
--- a/core/src/test/resources/backwardCompatibility/bc_1.yaml
+++ /dev/null
@@ -1,132 +0,0 @@
-openapi: 3.0.0
-servers:
-  - url: 'http://petstore.swagger.io/v2'
-info:
-  description: >-
-    This is a sample server Petstore server.  You can find out more about
-    Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net,
-    #swagger](http://swagger.io/irc/).  For this sample, you can use the api key
-    `special-key` to test the authorization filters.
-  version: 1.0.0
-  title: Swagger Petstore
-  termsOfService: 'http://swagger.io/terms/'
-  contact:
-    email: apiteam@swagger.io
-  license:
-    name: Apache 2.0
-    url: 'http://www.apache.org/licenses/LICENSE-2.0.html'
-tags:
-  - name: pet
-    description: Everything about your Pets
-    externalDocs:
-      description: Find out more
-      url: 'http://swagger.io'
-  - name: store
-    description: Access to Petstore orders
-  - name: user
-    description: Operations about user
-    externalDocs:
-      description: Find out more about our store
-      url: 'http://swagger.io'
-paths:
-  /pet/findByStatus:
-    get:
-      tags:
-        - pet
-      summary: Finds Pets by status
-      description: Multiple status values can be provided with comma separated strings
-      operationId: findPetsByStatus
-      parameters:
-        - name: status
-          in: query
-          description: Status values that need to be considered for filter
-          required: true
-          explode: true
-          schema:
-            type: array
-            items:
-              type: string
-              maxLength: 16
-      responses:
-        '200':
-          description: successful operation
-          content:
-            application/json:
-              schema:
-                type: object
-                properties:
-                  pets:
-                    type: array
-                    items:
-                      $ref: '#/components/schemas/Dog'
-        '400':
-          description: Invalid status value
-      security:
-        - petstore_auth:
-            - 'write:pets'
-            - 'read:pets'
-externalDocs:
-  description: Find out more about Swagger
-  url: 'http://swagger.io'
-components:
-  requestBodies:
-    Pet:
-      content:
-        application/json:
-          schema:
-            $ref: '#/components/schemas/Pet'
-      description: Pet object that needs to be added to the store
-      required: true
-  securitySchemes:
-    petstore_auth:
-      type: oauth2
-      flows:
-        implicit:
-          authorizationUrl: 'http://petstore.swagger.io/oauth/dialog'
-          scopes:
-            'write:pets': modify pets in your account
-            'read:pets': read your pets
-    api_key:
-      type: apiKey
-      name: api_key
-      in: header
-  schemas:
-    Pet:
-      type: object
-      required:
-      - pet_type
-      properties:
-        pet_type:
-          type: string
-      discriminator:
-        propertyName: pet_type
-        mapping:
-          cachorro: Dog
-    Cat:
-      type: object
-      properties:
-        name:
-          type: string
-    Dog:
-      type: object
-      properties:
-        bark:
-          type: string
-        test:
-          writeOnly: true
-          type: string
-    Lizard:
-      type: object
-      properties:
-        lovesRocks:
-          type: boolean
-
-    MyResponseType:
-      oneOf:
-      - $ref: '#/components/schemas/Cat'
-      - $ref: '#/components/schemas/Dog'
-      - $ref: '#/components/schemas/Lizard'
-      discriminator:
-        propertyName: pet_type
-        mapping:
-          dog: '#/components/schemas/Dog'
\ No newline at end of file
diff --git a/core/src/test/resources/backwardCompatibility/bc_2.yaml b/core/src/test/resources/backwardCompatibility/bc_2.yaml
deleted file mode 100644
index 179f3ca3f..000000000
--- a/core/src/test/resources/backwardCompatibility/bc_2.yaml
+++ /dev/null
@@ -1,150 +0,0 @@
-openapi: 3.0.0
-servers:
-  - url: 'http://petstore.swagger.io/v2'
-info:
-  description: >-
-    This is a sample server Petstore server.  You can find out more about
-    Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net,
-    #swagger](http://swagger.io/irc/).  For this sample, you can use the api key
-    `special-key` to test the authorization filters.
-  version: 1.0.0
-  title: Swagger Petstore
-  termsOfService: 'http://swagger.io/terms/'
-  contact:
-    email: apiteam@swagger.io
-  license:
-    name: Apache 2.0
-    url: 'http://www.apache.org/licenses/LICENSE-2.0.html'
-tags:
-  - name: pet
-    description: Everything about your Pets
-    externalDocs:
-      description: Find out more
-      url: 'http://swagger.io'
-  - name: store
-    description: Access to Petstore orders
-  - name: user
-    description: Operations about user
-    externalDocs:
-      description: Find out more about our store
-      url: 'http://swagger.io'
-paths:
-  /pet:
-    post:
-      tags:
-        - pet
-      summary: Add a new pet to the store
-      description: ''
-      operationId: addPet
-      requestBody:
-        $ref: '#/components/requestBodies/Pet'
-      responses:
-        '200':
-          description: successful operation
-          content:
-            application/json:
-              schema:
-                $ref: '#/components/schemas/MyResponseType'
-        '405':
-          description: Invalid input
-  /pet/findByStatus:
-    get:
-      tags:
-        - pet
-      summary: Finds Pets by status
-      description: Multiple status values can be provided with comma separated strings
-      operationId: findPetsByStatus
-      parameters:
-        - name: status
-          in: query
-          description: Status values that need to be considered for filter
-          required: true
-          explode: true
-          schema:
-            type: array
-            items:
-              type: string
-              maxLength: 24
-      responses:
-        '200':
-          description: successful operation
-          content:
-            application/json:
-              schema:
-                type: object
-                properties:
-                  pets:
-                    type: array
-                    items:
-                      $ref: '#/components/schemas/Dog'
-        '400':
-          description: Invalid status value
-      security:
-        - petstore_auth:
-            - 'write:pets'
-            - 'read:pets'
-externalDocs:
-  description: Find out more about Swagger
-  url: 'http://swagger.io'
-components:
-  requestBodies:
-    Pet:
-      content:
-        application/json:
-          schema:
-            $ref: '#/components/schemas/Pet'
-      description: Pet object that needs to be added to the store
-      required: true
-  securitySchemes:
-    petstore_auth:
-      type: oauth2
-      flows:
-        implicit:
-          authorizationUrl: 'http://petstore.swagger.io/oauth/dialog'
-          scopes:
-            'write:pets': modify pets in your account
-            'read:pets': read your pets
-    api_key:
-      type: apiKey
-      name: api_key
-      in: header
-  schemas:
-    Pet:
-      type: object
-      required:
-      - pet_type
-      properties:
-        pet_type:
-          type: string
-      discriminator:
-        propertyName: pet_type
-        mapping:
-          cachorro: Dog
-    Cat:
-      type: object
-      properties:
-        name:
-          type: string
-    Dog:
-      type: object
-      properties:
-        bark:
-          type: string
-        test:
-          writeOnly: true
-          type: string
-    Lizard:
-      type: object
-      properties:
-        lovesRocks:
-          type: boolean
-
-    MyResponseType:
-      oneOf:
-      - $ref: '#/components/schemas/Cat'
-      - $ref: '#/components/schemas/Dog'
-      - $ref: '#/components/schemas/Lizard'
-      discriminator:
-        propertyName: pet_type
-        mapping:
-          dog: '#/components/schemas/Dog'
\ No newline at end of file
diff --git a/core/src/test/resources/backwardCompatibility/bc_3.yaml b/core/src/test/resources/backwardCompatibility/bc_3.yaml
deleted file mode 100644
index 30a68ced7..000000000
--- a/core/src/test/resources/backwardCompatibility/bc_3.yaml
+++ /dev/null
@@ -1,163 +0,0 @@
-openapi: 3.0.0
-servers:
-  - url: 'http://petstore.swagger.io/v2'
-info:
-  description: >-
-    This is a sample server Petstore server.  You can find out more about
-    Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net,
-    #swagger](http://swagger.io/irc/).  For this sample, you can use the api key
-    `special-key` to test the authorization filters.
-  version: 1.0.0
-  title: Swagger Petstore
-  termsOfService: 'http://swagger.io/terms/'
-  contact:
-    email: apiteam@swagger.io
-  license:
-    name: Apache 2.0
-    url: 'http://www.apache.org/licenses/LICENSE-2.0.html'
-tags:
-  - name: pet
-    description: Everything about your Pets
-    externalDocs:
-      description: Find out more
-      url: 'http://swagger.io'
-  - name: store
-    description: Access to Petstore orders
-  - name: user
-    description: Operations about user
-    externalDocs:
-      description: Find out more about our store
-      url: 'http://swagger.io'
-paths:
-  /pet:
-    post:
-      tags:
-        - pet
-      summary: Add a new pet to the store
-      description: ''
-      operationId: addPet
-      requestBody:
-        $ref: '#/components/requestBodies/Pet'
-      responses:
-        '200':
-          description: successful operation
-          content:
-            application/json:
-              schema:
-                $ref: '#/components/schemas/MyResponseType'
-        '405':
-          description: Invalid input
-    get:
-      tags:
-        - pet
-      summary: Finds Pets by name
-      description: name can be provided for the pet
-      operationId: getPet
-      parameters:
-        - name: name
-          in: query
-          description: name that need to be considered for filter
-          required: true
-          schema:
-            type: string
-  /pet/findByStatus:
-    get:
-      tags:
-        - pet
-      summary: Finds Pets by status
-      description: Multiple status values can be provided with comma separated strings
-      operationId: findPetsByStatus
-      parameters:
-        - name: status
-          in: query
-          description: Status values that need to be considered for filter
-          required: true
-          explode: true
-          schema:
-            type: array
-            items:
-              type: string
-              maxLength: 36
-      responses:
-        '200':
-          description: successful operation
-          content:
-            application/json:
-              schema:
-                type: object
-                properties:
-                  pets:
-                    type: array
-                    items:
-                      $ref: '#/components/schemas/Dog'
-        '400':
-          description: Invalid status value
-      security:
-        - petstore_auth:
-            - 'write:pets'
-            - 'read:pets'
-externalDocs:
-  description: Find out more about Swagger
-  url: 'http://swagger.io'
-components:
-  requestBodies:
-    Pet:
-      content:
-        application/json:
-          schema:
-            $ref: '#/components/schemas/Pet'
-      description: Pet object that needs to be added to the store
-      required: true
-  securitySchemes:
-    petstore_auth:
-      type: oauth2
-      flows:
-        implicit:
-          authorizationUrl: 'http://petstore.swagger.io/oauth/dialog'
-          scopes:
-            'write:pets': modify pets in your account
-            'read:pets': read your pets
-    api_key:
-      type: apiKey
-      name: api_key
-      in: header
-  schemas:
-    Pet:
-      type: object
-      required:
-      - pet_type
-      properties:
-        pet_type:
-          type: string
-      discriminator:
-        propertyName: pet_type
-        mapping:
-          cachorro: Dog
-    Cat:
-      type: object
-      properties:
-        name:
-          type: string
-    Dog:
-      type: object
-      properties:
-        bark:
-          type: string
-        test:
-          writeOnly: true
-          type: string
-    Lizard:
-      type: object
-      properties:
-        lovesRocks:
-          type: boolean
-
-    MyResponseType:
-      oneOf:
-      - $ref: '#/components/schemas/Cat'
-      - $ref: '#/components/schemas/Dog'
-      - $ref: '#/components/schemas/Lizard'
-      discriminator:
-        propertyName: pet_type
-        mapping:
-          dog: '#/components/schemas/Dog'
\ No newline at end of file
diff --git a/core/src/test/resources/backwardCompatibility/bc_4.yaml b/core/src/test/resources/backwardCompatibility/bc_4.yaml
deleted file mode 100644
index 4e6ee3e18..000000000
--- a/core/src/test/resources/backwardCompatibility/bc_4.yaml
+++ /dev/null
@@ -1,155 +0,0 @@
-openapi: 3.0.0
-servers:
-  - url: 'http://petstore.swagger.io/v2'
-info:
-  description: >-
-    This is a sample server Petstore server.  You can find out more about
-    Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net,
-    #swagger](http://swagger.io/irc/).  For this sample, you can use the api key
-    `special-key` to test the authorization filters.
-  version: 1.0.0
-  title: Swagger Petstore
-  termsOfService: 'http://swagger.io/terms/'
-  contact:
-    email: apiteam@swagger.io
-  license:
-    name: Apache 2.0
-    url: 'http://www.apache.org/licenses/LICENSE-2.0.html'
-tags:
-  - name: pet
-    description: Everything about your Pets
-    externalDocs:
-      description: Find out more
-      url: 'http://swagger.io'
-  - name: store
-    description: Access to Petstore orders
-  - name: user
-    description: Operations about user
-    externalDocs:
-      description: Find out more about our store
-      url: 'http://swagger.io'
-paths:
-  /pet:
-    post:
-      tags:
-        - pet
-      summary: Add a new pet to the store
-      description: ''
-      operationId: addPet
-      requestBody:
-        $ref: '#/components/requestBodies/Pet'
-      responses:
-        '200':
-          description: successful operation
-          content:
-            application/json:
-              schema:
-                $ref: '#/components/schemas/MyResponseType'
-        '405':
-          description: Invalid input
-  /pet/findByStatus:
-    get:
-      tags:
-        - pet
-      summary: Finds Pets by status
-      description: Multiple status values can be provided with comma separated strings
-      operationId: findPetsByStatus
-      parameters:
-        - name: status
-          in: query
-          description: Status values that need to be considered for filter
-          required: true
-          explode: true
-          schema:
-            type: array
-            items:
-              type: string
-              enum:
-                - available
-                - pending
-                - sold
-              default: available
-      responses:
-        '200':
-          description: successful operation
-          content:
-            application/json:
-              schema:
-                type: object
-                properties:
-                  pets:
-                    type: array
-                    items:
-                      $ref: '#/components/schemas/Dog'
-        '400':
-          description: Invalid status value
-      security:
-        - petstore_auth:
-            - 'write:pets'
-            - 'read:pets'
-externalDocs:
-  description: Find out more about Swagger
-  url: 'http://swagger.io'
-components:
-  requestBodies:
-    Pet:
-      content:
-        application/json:
-          schema:
-            $ref: '#/components/schemas/Pet'
-      description: Pet object that needs to be added to the store
-      required: true
-  securitySchemes:
-    petstore_auth:
-      type: oauth2
-      flows:
-        implicit:
-          authorizationUrl: 'http://petstore.swagger.io/oauth/dialog'
-          scopes:
-            'write:pets': modify pets in your account
-            'read:pets': read your pets
-    api_key:
-      type: apiKey
-      name: api_key
-      in: header
-  schemas:
-    Pet:
-      type: object
-      required:
-      - pet_type
-      properties:
-        pet_type:
-          type: string
-      discriminator:
-        propertyName: pet_type
-        mapping:
-          cachorro: Dog
-    Cat:
-      type: object
-      properties:
-        name:
-          type: string
-          deprecated: true
-    Dog:
-      type: object
-      properties:
-        bark:
-          type: string
-        test:
-          writeOnly: true
-          type: string
-    Lizard:
-      type: object
-      properties:
-        lovesRocks:
-          type: boolean
-
-    MyResponseType:
-      oneOf:
-      - $ref: '#/components/schemas/Cat'
-      - $ref: '#/components/schemas/Dog'
-      - $ref: '#/components/schemas/Lizard'
-      discriminator:
-        propertyName: pet_type
-        mapping:
-          dog: '#/components/schemas/Dog'
\ No newline at end of file
diff --git a/core/src/test/resources/backwardCompatibility/bc_5.yaml b/core/src/test/resources/backwardCompatibility/bc_5.yaml
deleted file mode 100644
index 6b282ba48..000000000
--- a/core/src/test/resources/backwardCompatibility/bc_5.yaml
+++ /dev/null
@@ -1,131 +0,0 @@
-openapi: 3.0.0
-servers:
-  - url: 'http://petstore.swagger.io/v2'
-info:
-  description: >-
-    This is a sample server Petstore server.  You can find out more about
-    Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net,
-    #swagger](http://swagger.io/irc/).  For this sample, you can use the api key
-    `special-key` to test the authorization filters.
-  version: 1.0.0
-  title: Swagger Petstore
-  termsOfService: 'http://swagger.io/terms/'
-  contact:
-    email: apiteam@swagger.io
-  license:
-    name: Apache 2.0
-    url: 'http://www.apache.org/licenses/LICENSE-2.0.html'
-tags:
-  - name: pet
-    description: Everything about your Pets
-    externalDocs:
-      description: Find out more
-      url: 'http://swagger.io'
-  - name: store
-    description: Access to Petstore orders
-  - name: user
-    description: Operations about user
-    externalDocs:
-      description: Find out more about our store
-      url: 'http://swagger.io'
-paths:
-  /pet/findByStatus:
-    get:
-      tags:
-        - pet
-      summary: Finds Pets by status
-      description: Multiple status values can be provided with comma separated strings
-      operationId: findPetsByStatus
-      parameters:
-        - name: status
-          in: query
-          description: Status values that need to be considered for filter
-          required: true
-          explode: true
-          schema:
-            type: array
-            items:
-              type: string
-              maxLength: 16
-      responses:
-        '200':
-          description: successful operation
-          content:
-            application/json:
-              schema:
-                type: object
-                properties:
-                  pets:
-                    type: array
-                    items:
-                      $ref: '#/components/schemas/Dog'
-        '400':
-          description: Invalid status value
-      security:
-        - petstore_auth:
-            - 'write:pets'
-            - 'read:pets'
-externalDocs:
-  description: Find out more about Swagger
-  url: 'http://swagger.io'
-components:
-  requestBodies:
-    Pet:
-      content:
-        application/json:
-          schema:
-            $ref: '#/components/schemas/Pet'
-      description: Pet object that needs to be added to the store
-      required: true
-  securitySchemes:
-    petstore_auth:
-      type: oauth2
-      flows:
-        implicit:
-          authorizationUrl: 'http://petstore.swagger.io/oauth/dialog'
-          scopes:
-            'write:pets': modify pets in your account
-            'read:pets': read your pets
-    api_key:
-      type: apiKey
-      name: api_key
-      in: header
-  schemas:
-    Pet:
-      type: object
-      required:
-      - pet_type
-      properties:
-        pet_type:
-          type: string
-      discriminator:
-        propertyName: pet_type
-        mapping:
-          cachorro: Dog
-    Cat:
-      type: object
-      properties:
-        name:
-          type: string
-    Dog:
-      type: object
-      properties:
-        bark:
-          type: string
-        test:
-          type: string
-    Lizard:
-      type: object
-      properties:
-        lovesRocks:
-          type: boolean
-
-    MyResponseType:
-      oneOf:
-      - $ref: '#/components/schemas/Cat'
-      - $ref: '#/components/schemas/Dog'
-      - $ref: '#/components/schemas/Lizard'
-      discriminator:
-        propertyName: pet_type
-        mapping:
-          dog: '#/components/schemas/Dog'
\ No newline at end of file
diff --git a/core/src/test/resources/bc_content_base.yaml b/core/src/test/resources/bc_content_base.yaml
new file mode 100644
index 000000000..82634f2a9
--- /dev/null
+++ b/core/src/test/resources/bc_content_base.yaml
@@ -0,0 +1,29 @@
+openapi: 3.0.0
+info:
+  description: myDesc
+  title: myTitle
+  version: 1.0.0
+paths:
+  /widgets:
+    post:
+      operationId: widgetCreate
+      requestBody:
+        content:
+          application/json:
+            schema:
+              type: integer
+              format: int32
+          application/text:
+            schema:
+              type: string
+      responses:
+        '200':
+          description: successful operation
+          content:
+            application/json:
+              schema:
+                type: integer
+                format: int32
+            application/text:
+              schema:
+                type: string
diff --git a/core/src/test/resources/bc_content_changed_but_compatible.yaml b/core/src/test/resources/bc_content_changed_but_compatible.yaml
new file mode 100644
index 000000000..8300fbad7
--- /dev/null
+++ b/core/src/test/resources/bc_content_changed_but_compatible.yaml
@@ -0,0 +1,35 @@
+openapi: 3.0.0
+info:
+  description: myDesc
+  title: myTitle
+  version: 1.0.0
+paths:
+  /widgets:
+    post:
+      operationId: widgetCreate
+      requestBody:
+        content:
+          application/json:
+            schema:
+              type: integer
+              format: int32
+          application/text:
+            schema:
+              type: string
+          application/xml:
+            schema:
+              type: string
+      responses:
+        '200':
+          description: successful operation
+          content:
+            application/json:
+              schema:
+                type: integer
+                format: int32
+            application/text:
+              schema:
+                type: string
+            application/xml:
+              schema:
+                type: string
diff --git a/core/src/test/resources/bc_enum_base.yaml b/core/src/test/resources/bc_enum_base.yaml
new file mode 100644
index 000000000..fe020824a
--- /dev/null
+++ b/core/src/test/resources/bc_enum_base.yaml
@@ -0,0 +1,33 @@
+openapi: 3.0.0
+info:
+  description: myDesc
+  title: myTitle
+  version: 1.0.0
+paths:
+  /widgets:
+    get:
+      operationId: listWidgets
+      parameters:
+        - name: param-inline-enum
+          in: query
+          required: true
+          schema:
+            type: string
+            enum:
+              - param-inline-enum-val-1
+              - param-inline-enum-val-2
+            default: param-inline-enum-val-1
+      responses:
+        '200':
+          description: successful operation
+          content:
+            application/json:
+              schema:
+                type: object
+                properties:
+                  enum-prop:
+                    type: string
+                    enum:
+                      - enum-prop-val-1
+                      - enum-prop-val-2
+                    default: enum-prop-val-1
diff --git a/core/src/test/resources/bc_enum_changed_but_compatible.yaml b/core/src/test/resources/bc_enum_changed_but_compatible.yaml
new file mode 100644
index 000000000..7fd9b1da6
--- /dev/null
+++ b/core/src/test/resources/bc_enum_changed_but_compatible.yaml
@@ -0,0 +1,33 @@
+openapi: 3.0.0
+info:
+  description: myDesc
+  title: myTitle
+  version: 1.0.0
+paths:
+  /widgets:
+    get:
+      operationId: listWidgets
+      parameters:
+        - name: param-inline-enum
+          in: query
+          required: true
+          schema:
+            type: string
+            enum:
+              - param-inline-enum-val-1
+              - param-inline-enum-val-2
+              - param-inline-enum-val-3
+            default: param-inline-enum-val-1
+      responses:
+        '200':
+          description: successful operation
+          content:
+            application/json:
+              schema:
+                type: object
+                properties:
+                  enum-prop:
+                    type: string
+                    enum:
+                      - enum-prop-val-1
+                    default: enum-prop-val-1
diff --git a/core/src/test/resources/bc_maxlength_base.yaml b/core/src/test/resources/bc_maxlength_base.yaml
new file mode 100644
index 000000000..583adca5d
--- /dev/null
+++ b/core/src/test/resources/bc_maxlength_base.yaml
@@ -0,0 +1,29 @@
+openapi: 3.0.0
+info:
+  description: myDesc
+  title: myTitle
+  version: 1.0.0
+paths:
+  /widgets:
+    post:
+      operationId: widgetCreate
+      requestBody:
+        content:
+          application/json:
+            schema:
+              type: string
+              maxLength: 16
+          application/xml:
+            schema:
+              type: string
+      responses:
+        '200':
+          description: successful operation
+          content:
+            application/json:
+              schema:
+                type: string
+                maxLength: 16
+            application/xml:
+              schema:
+                type: string
diff --git a/core/src/test/resources/bc_numericrange_base.yaml b/core/src/test/resources/bc_numericrange_base.yaml
new file mode 100644
index 000000000..e03c5939e
--- /dev/null
+++ b/core/src/test/resources/bc_numericrange_base.yaml
@@ -0,0 +1,55 @@
+openapi: 3.0.0
+info:
+  description: myDesc
+  title: myTitle
+  version: 1.0.0
+paths:
+  /widgets:
+    post:
+      operationId: widgetCreate
+      requestBody:
+        content:
+          application/json:
+            schema:
+              type: integer
+              format: int32
+              exclusiveMinimum: false
+              exclusiveMaximum: false
+          application/text:
+            schema:
+              type: integer
+              format: int32
+              minimum: 10
+              maximum: 20
+          application/xml:
+            schema:
+              type: integer
+              format: int32
+              minimum: 10
+              maximum: 20
+              exclusiveMinimum: true
+              exclusiveMaximum: true
+      responses:
+        '200':
+          description: successful operation
+          content:
+            application/json:
+              schema:
+                type: integer
+                format: int32
+                exclusiveMinimum: false
+                exclusiveMaximum: false
+            application/text:
+              schema:
+                type: integer
+                format: int32
+                minimum: 10
+                maximum: 20
+            application/xml:
+              schema:
+                type: integer
+                format: int32
+                minimum: 10
+                maximum: 20
+                exclusiveMinimum: true
+                exclusiveMaximum: true
diff --git a/core/src/test/resources/bc_numericrange_changed_but_compatible.yaml b/core/src/test/resources/bc_numericrange_changed_but_compatible.yaml
new file mode 100644
index 000000000..f7e5057f4
--- /dev/null
+++ b/core/src/test/resources/bc_numericrange_changed_but_compatible.yaml
@@ -0,0 +1,54 @@
+openapi: 3.0.0
+info:
+  description: myDesc
+  title: myTitle
+  version: 1.0.0
+paths:
+  /widgets:
+    post:
+      operationId: widgetCreate
+      requestBody:
+        content:
+          application/json:
+            schema:
+              type: integer
+              format: int32
+              exclusiveMinimum: false
+          application/text:
+            schema:
+              type: integer
+              format: int32
+              minimum: 9
+              maximum: 21
+          application/xml:
+            schema:
+              type: integer
+              format: int32
+              minimum: 10
+              maximum: 20
+              exclusiveMinimum: false
+      responses:
+        '200':
+          description: successful operation
+          content:
+            application/json:
+              schema:
+                type: integer
+                format: int32
+                exclusiveMinimum: true
+            application/text:
+              schema:
+                type: integer
+                format: int32
+                minimum: 10
+                maximum: 20
+                exclusiveMinimum: true
+                exclusiveMaximum: true
+            application/xml:
+              schema:
+                type: integer
+                format: int32
+                minimum: 11
+                maximum: 19
+                exclusiveMinimum: true
+                exclusiveMaximum: true
diff --git a/core/src/test/resources/bc_oauthflow_authorization_url_changed.yaml b/core/src/test/resources/bc_oauthflow_authorization_url_changed.yaml
new file mode 100644
index 000000000..d14957eea
--- /dev/null
+++ b/core/src/test/resources/bc_oauthflow_authorization_url_changed.yaml
@@ -0,0 +1,28 @@
+openapi: 3.0.0
+info:
+  description: myDesc
+  title: myTitle
+  version: 1.0.0
+paths:
+  /widgets:
+    get:
+      operationId: listWidgets
+      responses:
+        '200':
+          description: successful operation
+          content:
+            application/json:
+              schema:
+                type: string
+      security:
+        - oauth2-scheme-1: []
+components:
+  securitySchemes:
+    oauth2-scheme-1:
+      type: oauth2
+      flows:
+        implicit:
+          authorizationUrl: https://example.com/api/oauth/authorization2
+          refreshUrl: https://example.com/api/oauth/refresh
+          tokenUrl: https://example.com/api/oauth/token
+          scopes: {}
diff --git a/core/src/test/resources/bc_oauthflow_base.yaml b/core/src/test/resources/bc_oauthflow_base.yaml
new file mode 100644
index 000000000..40c7509b4
--- /dev/null
+++ b/core/src/test/resources/bc_oauthflow_base.yaml
@@ -0,0 +1,28 @@
+openapi: 3.0.0
+info:
+  description: myDesc
+  title: myTitle
+  version: 1.0.0
+paths:
+  /widgets:
+    get:
+      operationId: listWidgets
+      responses:
+        '200':
+          description: successful operation
+          content:
+            application/json:
+              schema:
+                type: string
+      security:
+        - oauth2-scheme-1: []
+components:
+  securitySchemes:
+    oauth2-scheme-1:
+      type: oauth2
+      flows:
+        implicit:
+          authorizationUrl: https://example.com/api/oauth/authorization
+          refreshUrl: https://example.com/api/oauth/refresh
+          tokenUrl: https://example.com/api/oauth/token
+          scopes: {}
diff --git a/core/src/test/resources/bc_oauthflow_refresh_url_changed.yaml b/core/src/test/resources/bc_oauthflow_refresh_url_changed.yaml
new file mode 100644
index 000000000..bde8f2285
--- /dev/null
+++ b/core/src/test/resources/bc_oauthflow_refresh_url_changed.yaml
@@ -0,0 +1,28 @@
+openapi: 3.0.0
+info:
+  description: myDesc
+  title: myTitle
+  version: 1.0.0
+paths:
+  /widgets:
+    get:
+      operationId: listWidgets
+      responses:
+        '200':
+          description: successful operation
+          content:
+            application/json:
+              schema:
+                type: string
+      security:
+        - oauth2-scheme-1: []
+components:
+  securitySchemes:
+    oauth2-scheme-1:
+      type: oauth2
+      flows:
+        implicit:
+          authorizationUrl: https://example.com/api/oauth/authorization
+          refreshUrl: https://example.com/api/oauth/refresh2
+          tokenUrl: https://example.com/api/oauth/token
+          scopes: {}
diff --git a/core/src/test/resources/bc_oauthflow_token_url_changed.yaml b/core/src/test/resources/bc_oauthflow_token_url_changed.yaml
new file mode 100644
index 000000000..6992e3c07
--- /dev/null
+++ b/core/src/test/resources/bc_oauthflow_token_url_changed.yaml
@@ -0,0 +1,28 @@
+openapi: 3.0.0
+info:
+  description: myDesc
+  title: myTitle
+  version: 1.0.0
+paths:
+  /widgets:
+    get:
+      operationId: listWidgets
+      responses:
+        '200':
+          description: successful operation
+          content:
+            application/json:
+              schema:
+                type: string
+      security:
+        - oauth2-scheme-1: []
+components:
+  securitySchemes:
+    oauth2-scheme-1:
+      type: oauth2
+      flows:
+        implicit:
+          authorizationUrl: https://example.com/api/oauth/authorization
+          refreshUrl: https://example.com/api/oauth/refresh
+          tokenUrl: https://example.com/api/oauth/token2
+          scopes: {}
diff --git a/core/src/test/resources/bc_oneof_base.yaml b/core/src/test/resources/bc_oneof_base.yaml
new file mode 100644
index 000000000..b5ef92c35
--- /dev/null
+++ b/core/src/test/resources/bc_oneof_base.yaml
@@ -0,0 +1,48 @@
+openapi: 3.0.0
+info:
+  description: myDesc
+  title: myTitle
+  version: 1.0.0
+paths:
+  /widgets:
+    post:
+      operationId: widgetCreate
+      requestBody:
+        content:
+          application/json:
+            schema:
+              $ref: '#/components/schemas/WidgetCreateRequest'
+      responses:
+        '200':
+          description: successful operation
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/WidgetCreateResponse'
+components:
+  schemas:
+    WidgetCreateRequest:
+      type: object
+      oneOf:
+        - $ref: '#/components/schemas/Doodad'
+        - $ref: '#/components/schemas/Gadget'
+    WidgetCreateResponse:
+      type: object
+      oneOf:
+        - $ref: '#/components/schemas/Doodad'
+        - $ref: '#/components/schemas/Gadget'
+    Doodad:
+      type: object
+      properties:
+        doodad_prop1:
+          type: string
+    Gadget:
+      type: object
+      properties:
+        gadget_prop1:
+          type: string
+    Gizmo:
+      type: object
+      properties:
+        gizmo_prop1:
+          type: string
diff --git a/core/src/test/resources/bc_oneof_changed_but_compatible.yaml b/core/src/test/resources/bc_oneof_changed_but_compatible.yaml
new file mode 100644
index 000000000..78468ebe0
--- /dev/null
+++ b/core/src/test/resources/bc_oneof_changed_but_compatible.yaml
@@ -0,0 +1,48 @@
+openapi: 3.0.0
+info:
+  description: myDesc
+  title: myTitle
+  version: 1.0.0
+paths:
+  /widgets:
+    post:
+      operationId: widgetCreate
+      requestBody:
+        content:
+          application/json:
+            schema:
+              $ref: '#/components/schemas/WidgetCreateRequest'
+      responses:
+        '200':
+          description: successful operation
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/WidgetCreateResponse'
+components:
+  schemas:
+    WidgetCreateRequest:
+      type: object
+      oneOf:
+        - $ref: '#/components/schemas/Doodad'
+        - $ref: '#/components/schemas/Gadget'
+        - $ref: '#/components/schemas/Gizmo'
+    WidgetCreateResponse:
+      type: object
+      oneOf:
+        - $ref: '#/components/schemas/Doodad'
+    Doodad:
+      type: object
+      properties:
+        doodad_prop1:
+          type: string
+    Gadget:
+      type: object
+      properties:
+        gadget_prop1:
+          type: string
+    Gizmo:
+      type: object
+      properties:
+        gizmo_prop1:
+          type: string
diff --git a/core/src/test/resources/bc_openapi_base.yaml b/core/src/test/resources/bc_openapi_base.yaml
new file mode 100644
index 000000000..78e86b943
--- /dev/null
+++ b/core/src/test/resources/bc_openapi_base.yaml
@@ -0,0 +1,25 @@
+openapi: 3.0.0
+info:
+  description: myDesc
+  title: myTitle
+  version: 1.0.0
+paths:
+  /widgets:
+    get:
+      operationId: listWidgets
+      responses:
+        '200':
+          description: successful operation
+          content:
+            application/json:
+              schema:
+                type: string
+    post:
+      operationId: widgetCreate
+      responses:
+        '200':
+          description: successful operation
+          content:
+            application/json:
+              schema:
+                type: string
diff --git a/core/src/test/resources/bc_openapi_changed_but_compatible.yaml b/core/src/test/resources/bc_openapi_changed_but_compatible.yaml
new file mode 100644
index 000000000..5b5dfffc1
--- /dev/null
+++ b/core/src/test/resources/bc_openapi_changed_but_compatible.yaml
@@ -0,0 +1,35 @@
+openapi: 3.0.0
+info:
+  description: myDesc
+  title: myTitle
+  version: 1.0.0
+paths:
+  /widgets:
+    get:
+      operationId: listWidgets
+      responses:
+        '200':
+          description: successful operation
+          content:
+            application/json:
+              schema:
+                type: string
+    post:
+      operationId: widgetCreate
+      responses:
+        '200':
+          description: successful operation
+          content:
+            application/json:
+              schema:
+                type: string
+  /widgets/{id}:
+    get:
+      operationId: getWidgets
+      responses:
+        '200':
+          description: successful operation
+          content:
+            application/json:
+              schema:
+                type: string
diff --git a/core/src/test/resources/bc_openapi_endpoints_decreased.yaml b/core/src/test/resources/bc_openapi_endpoints_decreased.yaml
new file mode 100644
index 000000000..bd33b69dd
--- /dev/null
+++ b/core/src/test/resources/bc_openapi_endpoints_decreased.yaml
@@ -0,0 +1,16 @@
+openapi: 3.0.0
+info:
+  description: myDesc
+  title: myTitle
+  version: 1.0.0
+paths:
+  /widgets:
+    post:
+      operationId: widgetCreate
+      responses:
+        '200':
+          description: successful operation
+          content:
+            application/json:
+              schema:
+                type: string
diff --git a/core/src/test/resources/bc_operation_base.yaml b/core/src/test/resources/bc_operation_base.yaml
new file mode 100644
index 000000000..eb6403307
--- /dev/null
+++ b/core/src/test/resources/bc_operation_base.yaml
@@ -0,0 +1,16 @@
+openapi: 3.0.0
+info:
+  description: myDesc
+  title: myTitle
+  version: 1.0.0
+paths:
+  /widgets:
+    get:
+      operationId: listWidgets
+      responses:
+        '200':
+          description: successful operation
+          content:
+            application/json:
+              schema:
+                type: string
diff --git a/core/src/test/resources/bc_operation_changed_but_compatible.yaml b/core/src/test/resources/bc_operation_changed_but_compatible.yaml
new file mode 100644
index 000000000..2d99878b8
--- /dev/null
+++ b/core/src/test/resources/bc_operation_changed_but_compatible.yaml
@@ -0,0 +1,17 @@
+openapi: 3.0.0
+info:
+  description: myDesc
+  title: myTitle
+  version: 1.0.0
+paths:
+  /widgets:
+    get:
+      deprecated: true
+      operationId: listWidgets
+      responses:
+        '200':
+          description: successful operation
+          content:
+            application/json:
+              schema:
+                type: string
diff --git a/core/src/test/resources/bc_path_base.yaml b/core/src/test/resources/bc_path_base.yaml
new file mode 100644
index 000000000..78e86b943
--- /dev/null
+++ b/core/src/test/resources/bc_path_base.yaml
@@ -0,0 +1,25 @@
+openapi: 3.0.0
+info:
+  description: myDesc
+  title: myTitle
+  version: 1.0.0
+paths:
+  /widgets:
+    get:
+      operationId: listWidgets
+      responses:
+        '200':
+          description: successful operation
+          content:
+            application/json:
+              schema:
+                type: string
+    post:
+      operationId: widgetCreate
+      responses:
+        '200':
+          description: successful operation
+          content:
+            application/json:
+              schema:
+                type: string
diff --git a/core/src/test/resources/bc_path_changed_but_compatible.yaml b/core/src/test/resources/bc_path_changed_but_compatible.yaml
new file mode 100644
index 000000000..d2ae98018
--- /dev/null
+++ b/core/src/test/resources/bc_path_changed_but_compatible.yaml
@@ -0,0 +1,34 @@
+openapi: 3.0.0
+info:
+  description: myDesc
+  title: myTitle
+  version: 1.0.0
+paths:
+  /widgets:
+    get:
+      operationId: listWidgets
+      responses:
+        '200':
+          description: successful operation
+          content:
+            application/json:
+              schema:
+                type: string
+    post:
+      operationId: widgetCreate
+      responses:
+        '200':
+          description: successful operation
+          content:
+            application/json:
+              schema:
+                type: string
+    put:
+      operationId: widgetUpdate
+      responses:
+        '200':
+          description: successful operation
+          content:
+            application/json:
+              schema:
+                type: string
diff --git a/core/src/test/resources/bc_path_ops_decreased.yaml b/core/src/test/resources/bc_path_ops_decreased.yaml
new file mode 100644
index 000000000..eb6403307
--- /dev/null
+++ b/core/src/test/resources/bc_path_ops_decreased.yaml
@@ -0,0 +1,16 @@
+openapi: 3.0.0
+info:
+  description: myDesc
+  title: myTitle
+  version: 1.0.0
+paths:
+  /widgets:
+    get:
+      operationId: listWidgets
+      responses:
+        '200':
+          description: successful operation
+          content:
+            application/json:
+              schema:
+                type: string
diff --git a/core/src/test/resources/bc_paths_base.yaml b/core/src/test/resources/bc_paths_base.yaml
new file mode 100644
index 000000000..451f66310
--- /dev/null
+++ b/core/src/test/resources/bc_paths_base.yaml
@@ -0,0 +1,26 @@
+openapi: 3.0.0
+info:
+  description: myDesc
+  title: myTitle
+  version: 1.0.0
+paths:
+  /widgets:
+    get:
+      operationId: listWidgets
+      responses:
+        '200':
+          description: successful operation
+          content:
+            application/json:
+              schema:
+                type: string
+  /widgets/{id}:
+    get:
+      operationId: getWidget
+      responses:
+        '200':
+          description: successful operation
+          content:
+            application/json:
+              schema:
+                type: string
diff --git a/core/src/test/resources/bc_paths_changed_but_compatible.yaml b/core/src/test/resources/bc_paths_changed_but_compatible.yaml
new file mode 100644
index 000000000..e2b94c228
--- /dev/null
+++ b/core/src/test/resources/bc_paths_changed_but_compatible.yaml
@@ -0,0 +1,36 @@
+openapi: 3.0.0
+info:
+  description: myDesc
+  title: myTitle
+  version: 1.0.0
+paths:
+  /widgets:
+    get:
+      operationId: listWidgets
+      responses:
+        '200':
+          description: successful operation
+          content:
+            application/json:
+              schema:
+                type: string
+  /widgets/{id}:
+    get:
+      operationId: getWidget
+      responses:
+        '200':
+          description: successful operation
+          content:
+            application/json:
+              schema:
+                type: string
+  /widgets/{id}/status:
+    get:
+      operationId: getWidgetStatus
+      responses:
+        '200':
+          description: successful operation
+          content:
+            application/json:
+              schema:
+                type: string
diff --git a/core/src/test/resources/bc_paths_decreased.yaml b/core/src/test/resources/bc_paths_decreased.yaml
new file mode 100644
index 000000000..eb6403307
--- /dev/null
+++ b/core/src/test/resources/bc_paths_decreased.yaml
@@ -0,0 +1,16 @@
+openapi: 3.0.0
+info:
+  description: myDesc
+  title: myTitle
+  version: 1.0.0
+paths:
+  /widgets:
+    get:
+      operationId: listWidgets
+      responses:
+        '200':
+          description: successful operation
+          content:
+            application/json:
+              schema:
+                type: string
diff --git a/core/src/test/resources/bc_readonly_base.yaml b/core/src/test/resources/bc_readonly_base.yaml
new file mode 100644
index 000000000..dc4889b1f
--- /dev/null
+++ b/core/src/test/resources/bc_readonly_base.yaml
@@ -0,0 +1,33 @@
+openapi: 3.0.0
+info:
+  description: myDesc
+  title: myTitle
+  version: 1.0.0
+paths:
+  /widgets:
+    post:
+      operationId: widgetCreate
+      requestBody:
+        content:
+          application/json:
+            schema:
+              type: object
+              properties:
+                prop1:
+                  type: string
+                  readOnly: true
+                prop2:
+                  type: string
+              required:
+                - prop1
+      responses:
+        '200':
+          description: successful operation
+          content:
+            application/json:
+              schema:
+                type: object
+                properties:
+                  prop1:
+                    type: string
+                    readOnly: true
diff --git a/core/src/test/resources/bc_readonly_changed_but_compatible.yaml b/core/src/test/resources/bc_readonly_changed_but_compatible.yaml
new file mode 100644
index 000000000..cc6596ef6
--- /dev/null
+++ b/core/src/test/resources/bc_readonly_changed_but_compatible.yaml
@@ -0,0 +1,32 @@
+openapi: 3.0.0
+info:
+  description: myDesc
+  title: myTitle
+  version: 1.0.0
+paths:
+  /widgets:
+    post:
+      operationId: widgetCreate
+      requestBody:
+        content:
+          application/json:
+            schema:
+              type: object
+              properties:
+                prop1:
+                  type: string
+                  readOnly: true
+                prop2:
+                  type: string
+              required:
+                - prop1
+      responses:
+        '200':
+          description: successful operation
+          content:
+            application/json:
+              schema:
+                type: object
+                properties:
+                  prop1:
+                    type: string
diff --git a/core/src/test/resources/bc_request_body_base.yaml b/core/src/test/resources/bc_request_body_base.yaml
new file mode 100644
index 000000000..fd89553e9
--- /dev/null
+++ b/core/src/test/resources/bc_request_body_base.yaml
@@ -0,0 +1,21 @@
+openapi: 3.0.0
+info:
+  description: myDesc
+  title: myTitle
+  version: 1.0.0
+paths:
+  /widgets:
+    post:
+      operationId: widgetCreate
+      requestBody:
+        content:
+          application/json:
+            schema:
+              type: string
+      responses:
+        '200':
+          description: successful operation
+          content:
+            application/json:
+              schema:
+                type: string
diff --git a/core/src/test/resources/bc_request_body_required_changed.yaml b/core/src/test/resources/bc_request_body_required_changed.yaml
new file mode 100644
index 000000000..859512ca9
--- /dev/null
+++ b/core/src/test/resources/bc_request_body_required_changed.yaml
@@ -0,0 +1,22 @@
+openapi: 3.0.0
+info:
+  description: myDesc
+  title: myTitle
+  version: 1.0.0
+paths:
+  /widgets:
+    post:
+      operationId: widgetCreate
+      requestBody:
+        content:
+          application/json:
+            schema:
+              type: string
+        required: true
+      responses:
+        '200':
+          description: successful operation
+          content:
+            application/json:
+              schema:
+                type: string
diff --git a/core/src/test/resources/bc_request_content_decreased.yaml b/core/src/test/resources/bc_request_content_decreased.yaml
new file mode 100644
index 000000000..d7a5517de
--- /dev/null
+++ b/core/src/test/resources/bc_request_content_decreased.yaml
@@ -0,0 +1,26 @@
+openapi: 3.0.0
+info:
+  description: myDesc
+  title: myTitle
+  version: 1.0.0
+paths:
+  /widgets:
+    post:
+      operationId: widgetCreate
+      requestBody:
+        content:
+          application/json:
+            schema:
+              type: integer
+              format: int32
+      responses:
+        '200':
+          description: successful operation
+          content:
+            application/json:
+              schema:
+                type: integer
+                format: int32
+            application/text:
+              schema:
+                type: string
diff --git a/core/src/test/resources/bc_request_enum_decreased.yaml b/core/src/test/resources/bc_request_enum_decreased.yaml
new file mode 100644
index 000000000..c131e9aa7
--- /dev/null
+++ b/core/src/test/resources/bc_request_enum_decreased.yaml
@@ -0,0 +1,32 @@
+openapi: 3.0.0
+info:
+  description: myDesc
+  title: myTitle
+  version: 1.0.0
+paths:
+  /widgets:
+    get:
+      operationId: listWidgets
+      parameters:
+        - name: param-inline-enum
+          in: query
+          required: true
+          schema:
+            type: string
+            enum:
+              - param-inline-enum-val-1
+            default: param-inline-enum-val-1
+      responses:
+        '200':
+          description: successful operation
+          content:
+            application/json:
+              schema:
+                type: object
+                properties:
+                  enum-prop:
+                    type: string
+                    enum:
+                      - enum-prop-val-1
+                      - enum-prop-val-2
+                    default: enum-prop-val-1
diff --git a/core/src/test/resources/bc_request_maxlength_decreased.yaml b/core/src/test/resources/bc_request_maxlength_decreased.yaml
new file mode 100644
index 000000000..48d50863c
--- /dev/null
+++ b/core/src/test/resources/bc_request_maxlength_decreased.yaml
@@ -0,0 +1,30 @@
+openapi: 3.0.0
+info:
+  description: myDesc
+  title: myTitle
+  version: 1.0.0
+paths:
+  /widgets:
+    post:
+      operationId: widgetCreate
+      requestBody:
+        content:
+          application/json:
+            schema:
+              type: string
+              maxLength: 10
+          application/xml:
+            schema:
+              type: string
+              maxLength: 10
+      responses:
+        '200':
+          description: successful operation
+          content:
+            application/json:
+              schema:
+                type: string
+                maxLength: 16
+            application/xml:
+              schema:
+                type: string
diff --git a/core/src/test/resources/bc_request_numericrange_exclusive_max_created.yaml b/core/src/test/resources/bc_request_numericrange_exclusive_max_created.yaml
new file mode 100644
index 000000000..f893a9545
--- /dev/null
+++ b/core/src/test/resources/bc_request_numericrange_exclusive_max_created.yaml
@@ -0,0 +1,56 @@
+openapi: 3.0.0
+info:
+  description: myDesc
+  title: myTitle
+  version: 1.0.0
+paths:
+  /widgets:
+    post:
+      operationId: widgetCreate
+      requestBody:
+        content:
+          application/json:
+            schema:
+              type: integer
+              format: int32
+              exclusiveMinimum: false
+              exclusiveMaximum: false
+          application/text:
+            schema:
+              type: integer
+              format: int32
+              minimum: 10
+              maximum: 20
+              exclusiveMaximum: true
+          application/xml:
+            schema:
+              type: integer
+              format: int32
+              minimum: 10
+              maximum: 20
+              exclusiveMinimum: true
+              exclusiveMaximum: true
+      responses:
+        '200':
+          description: successful operation
+          content:
+            application/json:
+              schema:
+                type: integer
+                format: int32
+                exclusiveMinimum: false
+                exclusiveMaximum: false
+            application/text:
+              schema:
+                type: integer
+                format: int32
+                minimum: 10
+                maximum: 20
+            application/xml:
+              schema:
+                type: integer
+                format: int32
+                minimum: 10
+                maximum: 20
+                exclusiveMinimum: true
+                exclusiveMaximum: true
diff --git a/core/src/test/resources/bc_request_numericrange_exclusive_max_set.yaml b/core/src/test/resources/bc_request_numericrange_exclusive_max_set.yaml
new file mode 100644
index 000000000..efa02f990
--- /dev/null
+++ b/core/src/test/resources/bc_request_numericrange_exclusive_max_set.yaml
@@ -0,0 +1,55 @@
+openapi: 3.0.0
+info:
+  description: myDesc
+  title: myTitle
+  version: 1.0.0
+paths:
+  /widgets:
+    post:
+      operationId: widgetCreate
+      requestBody:
+        content:
+          application/json:
+            schema:
+              type: integer
+              format: int32
+              exclusiveMinimum: false
+              exclusiveMaximum: true
+          application/text:
+            schema:
+              type: integer
+              format: int32
+              minimum: 10
+              maximum: 20
+          application/xml:
+            schema:
+              type: integer
+              format: int32
+              minimum: 10
+              maximum: 20
+              exclusiveMinimum: true
+              exclusiveMaximum: true
+      responses:
+        '200':
+          description: successful operation
+          content:
+            application/json:
+              schema:
+                type: integer
+                format: int32
+                exclusiveMinimum: false
+                exclusiveMaximum: false
+            application/text:
+              schema:
+                type: integer
+                format: int32
+                minimum: 10
+                maximum: 20
+            application/xml:
+              schema:
+                type: integer
+                format: int32
+                minimum: 10
+                maximum: 20
+                exclusiveMinimum: true
+                exclusiveMaximum: true
diff --git a/core/src/test/resources/bc_request_numericrange_exclusive_min_created.yaml b/core/src/test/resources/bc_request_numericrange_exclusive_min_created.yaml
new file mode 100644
index 000000000..649b7f48b
--- /dev/null
+++ b/core/src/test/resources/bc_request_numericrange_exclusive_min_created.yaml
@@ -0,0 +1,56 @@
+openapi: 3.0.0
+info:
+  description: myDesc
+  title: myTitle
+  version: 1.0.0
+paths:
+  /widgets:
+    post:
+      operationId: widgetCreate
+      requestBody:
+        content:
+          application/json:
+            schema:
+              type: integer
+              format: int32
+              exclusiveMinimum: false
+              exclusiveMaximum: false
+          application/text:
+            schema:
+              type: integer
+              format: int32
+              minimum: 10
+              maximum: 20
+              exclusiveMinimum: true
+          application/xml:
+            schema:
+              type: integer
+              format: int32
+              minimum: 10
+              maximum: 20
+              exclusiveMinimum: true
+              exclusiveMaximum: true
+      responses:
+        '200':
+          description: successful operation
+          content:
+            application/json:
+              schema:
+                type: integer
+                format: int32
+                exclusiveMinimum: false
+                exclusiveMaximum: false
+            application/text:
+              schema:
+                type: integer
+                format: int32
+                minimum: 10
+                maximum: 20
+            application/xml:
+              schema:
+                type: integer
+                format: int32
+                minimum: 10
+                maximum: 20
+                exclusiveMinimum: true
+                exclusiveMaximum: true
diff --git a/core/src/test/resources/bc_request_numericrange_exclusive_min_set.yaml b/core/src/test/resources/bc_request_numericrange_exclusive_min_set.yaml
new file mode 100644
index 000000000..e6d0c81fb
--- /dev/null
+++ b/core/src/test/resources/bc_request_numericrange_exclusive_min_set.yaml
@@ -0,0 +1,55 @@
+openapi: 3.0.0
+info:
+  description: myDesc
+  title: myTitle
+  version: 1.0.0
+paths:
+  /widgets:
+    post:
+      operationId: widgetCreate
+      requestBody:
+        content:
+          application/json:
+            schema:
+              type: integer
+              format: int32
+              exclusiveMinimum: true
+              exclusiveMaximum: false
+          application/text:
+            schema:
+              type: integer
+              format: int32
+              minimum: 10
+              maximum: 20
+          application/xml:
+            schema:
+              type: integer
+              format: int32
+              minimum: 10
+              maximum: 20
+              exclusiveMinimum: true
+              exclusiveMaximum: true
+      responses:
+        '200':
+          description: successful operation
+          content:
+            application/json:
+              schema:
+                type: integer
+                format: int32
+                exclusiveMinimum: false
+                exclusiveMaximum: false
+            application/text:
+              schema:
+                type: integer
+                format: int32
+                minimum: 10
+                maximum: 20
+            application/xml:
+              schema:
+                type: integer
+                format: int32
+                minimum: 10
+                maximum: 20
+                exclusiveMinimum: true
+                exclusiveMaximum: true
diff --git a/core/src/test/resources/bc_request_numericrange_max_added.yaml b/core/src/test/resources/bc_request_numericrange_max_added.yaml
new file mode 100644
index 000000000..f438d9bda
--- /dev/null
+++ b/core/src/test/resources/bc_request_numericrange_max_added.yaml
@@ -0,0 +1,56 @@
+openapi: 3.0.0
+info:
+  description: myDesc
+  title: myTitle
+  version: 1.0.0
+paths:
+  /widgets:
+    post:
+      operationId: widgetCreate
+      requestBody:
+        content:
+          application/json:
+            schema:
+              type: integer
+              format: int32
+              maximum: 20
+              exclusiveMinimum: false
+              exclusiveMaximum: false
+          application/text:
+            schema:
+              type: integer
+              format: int32
+              minimum: 10
+              maximum: 20
+          application/xml:
+            schema:
+              type: integer
+              format: int32
+              minimum: 10
+              maximum: 20
+              exclusiveMinimum: true
+              exclusiveMaximum: true
+      responses:
+        '200':
+          description: successful operation
+          content:
+            application/json:
+              schema:
+                type: integer
+                format: int32
+                exclusiveMinimum: false
+                exclusiveMaximum: false
+            application/text:
+              schema:
+                type: integer
+                format: int32
+                minimum: 10
+                maximum: 20
+            application/xml:
+              schema:
+                type: integer
+                format: int32
+                minimum: 10
+                maximum: 20
+                exclusiveMinimum: true
+                exclusiveMaximum: true
diff --git a/core/src/test/resources/bc_request_numericrange_max_decreased.yaml b/core/src/test/resources/bc_request_numericrange_max_decreased.yaml
new file mode 100644
index 000000000..be0dec2c6
--- /dev/null
+++ b/core/src/test/resources/bc_request_numericrange_max_decreased.yaml
@@ -0,0 +1,55 @@
+openapi: 3.0.0
+info:
+  description: myDesc
+  title: myTitle
+  version: 1.0.0
+paths:
+  /widgets:
+    post:
+      operationId: widgetCreate
+      requestBody:
+        content:
+          application/json:
+            schema:
+              type: integer
+              format: int32
+              exclusiveMinimum: false
+              exclusiveMaximum: false
+          application/text:
+            schema:
+              type: integer
+              format: int32
+              minimum: 10
+              maximum: 19
+          application/xml:
+            schema:
+              type: integer
+              format: int32
+              minimum: 10
+              maximum: 19
+              exclusiveMinimum: true
+              exclusiveMaximum: true
+      responses:
+        '200':
+          description: successful operation
+          content:
+            application/json:
+              schema:
+                type: integer
+                format: int32
+                exclusiveMinimum: false
+                exclusiveMaximum: false
+            application/text:
+              schema:
+                type: integer
+                format: int32
+                minimum: 10
+                maximum: 20
+            application/xml:
+              schema:
+                type: integer
+                format: int32
+                minimum: 10
+                maximum: 20
+                exclusiveMinimum: true
+                exclusiveMaximum: true
diff --git a/core/src/test/resources/bc_request_numericrange_min_added.yaml b/core/src/test/resources/bc_request_numericrange_min_added.yaml
new file mode 100644
index 000000000..84a06bbca
--- /dev/null
+++ b/core/src/test/resources/bc_request_numericrange_min_added.yaml
@@ -0,0 +1,56 @@
+openapi: 3.0.0
+info:
+  description: myDesc
+  title: myTitle
+  version: 1.0.0
+paths:
+  /widgets:
+    post:
+      operationId: widgetCreate
+      requestBody:
+        content:
+          application/json:
+            schema:
+              type: integer
+              format: int32
+              minimum: 10
+              exclusiveMinimum: false
+              exclusiveMaximum: false
+          application/text:
+            schema:
+              type: integer
+              format: int32
+              minimum: 10
+              maximum: 20
+          application/xml:
+            schema:
+              type: integer
+              format: int32
+              minimum: 10
+              maximum: 20
+              exclusiveMinimum: true
+              exclusiveMaximum: true
+      responses:
+        '200':
+          description: successful operation
+          content:
+            application/json:
+              schema:
+                type: integer
+                format: int32
+                exclusiveMinimum: false
+                exclusiveMaximum: false
+            application/text:
+              schema:
+                type: integer
+                format: int32
+                minimum: 10
+                maximum: 20
+            application/xml:
+              schema:
+                type: integer
+                format: int32
+                minimum: 10
+                maximum: 20
+                exclusiveMinimum: true
+                exclusiveMaximum: true
diff --git a/core/src/test/resources/bc_request_numericrange_min_increased.yaml b/core/src/test/resources/bc_request_numericrange_min_increased.yaml
new file mode 100644
index 000000000..87df1ba4a
--- /dev/null
+++ b/core/src/test/resources/bc_request_numericrange_min_increased.yaml
@@ -0,0 +1,55 @@
+openapi: 3.0.0
+info:
+  description: myDesc
+  title: myTitle
+  version: 1.0.0
+paths:
+  /widgets:
+    post:
+      operationId: widgetCreate
+      requestBody:
+        content:
+          application/json:
+            schema:
+              type: integer
+              format: int32
+              exclusiveMinimum: false
+              exclusiveMaximum: false
+          application/text:
+            schema:
+              type: integer
+              format: int32
+              minimum: 11
+              maximum: 20
+          application/xml:
+            schema:
+              type: integer
+              format: int32
+              minimum: 11
+              maximum: 20
+              exclusiveMinimum: true
+              exclusiveMaximum: true
+      responses:
+        '200':
+          description: successful operation
+          content:
+            application/json:
+              schema:
+                type: integer
+                format: int32
+                exclusiveMinimum: false
+                exclusiveMaximum: false
+            application/text:
+              schema:
+                type: integer
+                format: int32
+                minimum: 10
+                maximum: 20
+            application/xml:
+              schema:
+                type: integer
+                format: int32
+                minimum: 10
+                maximum: 20
+                exclusiveMinimum: true
+                exclusiveMaximum: true
diff --git a/core/src/test/resources/bc_request_oneof_decreased.yaml b/core/src/test/resources/bc_request_oneof_decreased.yaml
new file mode 100644
index 000000000..82371aed6
--- /dev/null
+++ b/core/src/test/resources/bc_request_oneof_decreased.yaml
@@ -0,0 +1,47 @@
+openapi: 3.0.0
+info:
+  description: myDesc
+  title: myTitle
+  version: 1.0.0
+paths:
+  /widgets:
+    post:
+      operationId: widgetCreate
+      requestBody:
+        content:
+          application/json:
+            schema:
+              $ref: '#/components/schemas/WidgetCreateRequest'
+      responses:
+        '200':
+          description: successful operation
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/WidgetCreateResponse'
+components:
+  schemas:
+    WidgetCreateRequest:
+      type: object
+      oneOf:
+        - $ref: '#/components/schemas/Doodad'
+    WidgetCreateResponse:
+      type: object
+      oneOf:
+        - $ref: '#/components/schemas/Doodad'
+        - $ref: '#/components/schemas/Gadget'
+    Doodad:
+      type: object
+      properties:
+        doodad_prop1:
+          type: string
+    Gadget:
+      type: object
+      properties:
+        gadget_prop1:
+          type: string
+    Gizmo:
+      type: object
+      properties:
+        gizmo_prop1:
+          type: string
diff --git a/core/src/test/resources/bc_request_param_allowemptyvalue_decreased.yaml b/core/src/test/resources/bc_request_param_allowemptyvalue_decreased.yaml
new file mode 100644
index 000000000..cf25dd898
--- /dev/null
+++ b/core/src/test/resources/bc_request_param_allowemptyvalue_decreased.yaml
@@ -0,0 +1,28 @@
+openapi: 3.0.0
+info:
+  description: myDesc
+  title: myTitle
+  version: 1.0.0
+paths:
+  /widgets:
+    get:
+      operationId: listWidgets
+      parameters:
+        - name: query-param-1
+          in: query
+          required: true
+          schema:
+            type: string
+        - name: query-param-2
+          in: query
+          style: form
+          explode: true
+          schema:
+            type: string
+      responses:
+        '200':
+          description: successful operation
+          content:
+            application/json:
+              schema:
+                type: string
diff --git a/core/src/test/resources/bc_request_param_base.yaml b/core/src/test/resources/bc_request_param_base.yaml
new file mode 100644
index 000000000..555015991
--- /dev/null
+++ b/core/src/test/resources/bc_request_param_base.yaml
@@ -0,0 +1,29 @@
+openapi: 3.0.0
+info:
+  description: myDesc
+  title: myTitle
+  version: 1.0.0
+paths:
+  /widgets:
+    get:
+      operationId: listWidgets
+      parameters:
+        - name: query-param-1
+          in: query
+          required: true
+          schema:
+            type: string
+        - name: query-param-2
+          in: query
+          style: form
+          explode: true
+          allowEmptyValue: true
+          schema:
+            type: string
+      responses:
+        '200':
+          description: successful operation
+          content:
+            application/json:
+              schema:
+                type: string
diff --git a/core/src/test/resources/bc_request_param_changed_but_compatible.yaml b/core/src/test/resources/bc_request_param_changed_but_compatible.yaml
new file mode 100644
index 000000000..281fd0514
--- /dev/null
+++ b/core/src/test/resources/bc_request_param_changed_but_compatible.yaml
@@ -0,0 +1,30 @@
+openapi: 3.0.0
+info:
+  description: myDesc
+  title: myTitle
+  version: 1.0.0
+paths:
+  /widgets:
+    get:
+      operationId: listWidgets
+      parameters:
+        - name: query-param-1
+          in: query
+          allowEmptyValue: true
+          required: false
+          schema:
+            type: string
+        - name: query-param-2
+          in: query
+          style: form
+          explode: true
+          allowEmptyValue: true
+          schema:
+            type: string
+      responses:
+        '200':
+          description: successful operation
+          content:
+            application/json:
+              schema:
+                type: string
diff --git a/core/src/test/resources/bc_request_param_explode_changed.yaml b/core/src/test/resources/bc_request_param_explode_changed.yaml
new file mode 100644
index 000000000..3424c5fbe
--- /dev/null
+++ b/core/src/test/resources/bc_request_param_explode_changed.yaml
@@ -0,0 +1,30 @@
+openapi: 3.0.0
+info:
+  description: myDesc
+  title: myTitle
+  version: 1.0.0
+paths:
+  /widgets:
+    get:
+      operationId: listWidgets
+      parameters:
+        - name: query-param-1
+          in: query
+          explode: true
+          required: true
+          schema:
+            type: string
+        - name: query-param-2
+          in: query
+          style: form
+          explode: false
+          allowEmptyValue: true
+          schema:
+            type: string
+      responses:
+        '200':
+          description: successful operation
+          content:
+            application/json:
+              schema:
+                type: string
diff --git a/core/src/test/resources/bc_request_param_required_increased.yaml b/core/src/test/resources/bc_request_param_required_increased.yaml
new file mode 100644
index 000000000..6af74a9bc
--- /dev/null
+++ b/core/src/test/resources/bc_request_param_required_increased.yaml
@@ -0,0 +1,30 @@
+openapi: 3.0.0
+info:
+  description: myDesc
+  title: myTitle
+  version: 1.0.0
+paths:
+  /widgets:
+    get:
+      operationId: listWidgets
+      parameters:
+        - name: query-param-1
+          in: query
+          required: true
+          schema:
+            type: string
+        - name: query-param-2
+          in: query
+          style: form
+          explode: true
+          allowEmptyValue: true
+          required: true
+          schema:
+            type: string
+      responses:
+        '200':
+          description: successful operation
+          content:
+            application/json:
+              schema:
+                type: string
diff --git a/core/src/test/resources/bc_request_param_style_changed.yaml b/core/src/test/resources/bc_request_param_style_changed.yaml
new file mode 100644
index 000000000..ff8cb1dba
--- /dev/null
+++ b/core/src/test/resources/bc_request_param_style_changed.yaml
@@ -0,0 +1,30 @@
+openapi: 3.0.0
+info:
+  description: myDesc
+  title: myTitle
+  version: 1.0.0
+paths:
+  /widgets:
+    get:
+      operationId: listWidgets
+      parameters:
+        - name: query-param-1
+          in: query
+          required: true
+          style: form
+          schema:
+            type: string
+        - name: query-param-2
+          in: query
+          style: simple
+          explode: true
+          allowEmptyValue: true
+          schema:
+            type: string
+      responses:
+        '200':
+          description: successful operation
+          content:
+            application/json:
+              schema:
+                type: string
diff --git a/core/src/test/resources/bc_request_params_base.yaml b/core/src/test/resources/bc_request_params_base.yaml
new file mode 100644
index 000000000..9d623821f
--- /dev/null
+++ b/core/src/test/resources/bc_request_params_base.yaml
@@ -0,0 +1,26 @@
+openapi: 3.0.0
+info:
+  description: myDesc
+  title: myTitle
+  version: 1.0.0
+paths:
+  /widgets:
+    get:
+      operationId: listWidgets
+      parameters:
+        - name: query-param-1
+          in: query
+          required: true
+          schema:
+            type: string
+        - name: query-param-2
+          in: query
+          schema:
+            type: string
+      responses:
+        '200':
+          description: successful operation
+          content:
+            application/json:
+              schema:
+                type: string
diff --git a/core/src/test/resources/bc_request_params_changed_but_compatible.yaml b/core/src/test/resources/bc_request_params_changed_but_compatible.yaml
new file mode 100644
index 000000000..62a26fbb6
--- /dev/null
+++ b/core/src/test/resources/bc_request_params_changed_but_compatible.yaml
@@ -0,0 +1,30 @@
+openapi: 3.0.0
+info:
+  description: myDesc
+  title: myTitle
+  version: 1.0.0
+paths:
+  /widgets:
+    get:
+      operationId: listWidgets
+      parameters:
+        - name: query-param-1
+          in: query
+          required: true
+          schema:
+            type: string
+        - name: query-param-2
+          in: query
+          schema:
+            type: string
+        - name: query-param-3
+          in: query
+          schema:
+            type: string
+      responses:
+        '200':
+          description: successful operation
+          content:
+            application/json:
+              schema:
+                type: string
diff --git a/core/src/test/resources/bc_request_params_decreased.yaml b/core/src/test/resources/bc_request_params_decreased.yaml
new file mode 100644
index 000000000..2837bcc4d
--- /dev/null
+++ b/core/src/test/resources/bc_request_params_decreased.yaml
@@ -0,0 +1,22 @@
+openapi: 3.0.0
+info:
+  description: myDesc
+  title: myTitle
+  version: 1.0.0
+paths:
+  /widgets:
+    get:
+      operationId: listWidgets
+      parameters:
+        - name: query-param-1
+          in: query
+          required: true
+          schema:
+            type: string
+      responses:
+        '200':
+          description: successful operation
+          content:
+            application/json:
+              schema:
+                type: string
diff --git a/core/src/test/resources/bc_request_params_required_increased.yaml b/core/src/test/resources/bc_request_params_required_increased.yaml
new file mode 100644
index 000000000..d1c2e16c3
--- /dev/null
+++ b/core/src/test/resources/bc_request_params_required_increased.yaml
@@ -0,0 +1,31 @@
+openapi: 3.0.0
+info:
+  description: myDesc
+  title: myTitle
+  version: 1.0.0
+paths:
+  /widgets:
+    get:
+      operationId: listWidgets
+      parameters:
+        - name: query-param-1
+          in: query
+          required: true
+          schema:
+            type: string
+        - name: query-param-2
+          in: query
+          schema:
+            type: string
+        - name: query-param-3
+          in: query
+          required: true
+          schema:
+            type: string
+      responses:
+        '200':
+          description: successful operation
+          content:
+            application/json:
+              schema:
+                type: string
diff --git a/core/src/test/resources/bc_request_readonly_increased.yaml b/core/src/test/resources/bc_request_readonly_increased.yaml
new file mode 100644
index 000000000..5de3149d1
--- /dev/null
+++ b/core/src/test/resources/bc_request_readonly_increased.yaml
@@ -0,0 +1,34 @@
+openapi: 3.0.0
+info:
+  description: myDesc
+  title: myTitle
+  version: 1.0.0
+paths:
+  /widgets:
+    post:
+      operationId: widgetCreate
+      requestBody:
+        content:
+          application/json:
+            schema:
+              type: object
+              properties:
+                prop1:
+                  type: string
+                  readOnly: true
+                prop2:
+                  type: string
+                  readOnly: true
+              required:
+                - prop1
+      responses:
+        '200':
+          description: successful operation
+          content:
+            application/json:
+              schema:
+                type: object
+                properties:
+                  prop1:
+                    type: string
+                    readOnly: true
diff --git a/core/src/test/resources/bc_request_readonly_required_decreased.yaml b/core/src/test/resources/bc_request_readonly_required_decreased.yaml
new file mode 100644
index 000000000..73f12cae5
--- /dev/null
+++ b/core/src/test/resources/bc_request_readonly_required_decreased.yaml
@@ -0,0 +1,32 @@
+openapi: 3.0.0
+info:
+  description: myDesc
+  title: myTitle
+  version: 1.0.0
+paths:
+  /widgets:
+    post:
+      operationId: widgetCreate
+      requestBody:
+        content:
+          application/json:
+            schema:
+              type: object
+              properties:
+                prop1:
+                  type: string
+                prop2:
+                  type: string
+              required:
+                - prop1
+      responses:
+        '200':
+          description: successful operation
+          content:
+            application/json:
+              schema:
+                type: object
+                properties:
+                  prop1:
+                    type: string
+                    readOnly: true
diff --git a/core/src/test/resources/bc_request_required_increased.yaml b/core/src/test/resources/bc_request_required_increased.yaml
new file mode 100644
index 000000000..cd9821ac0
--- /dev/null
+++ b/core/src/test/resources/bc_request_required_increased.yaml
@@ -0,0 +1,42 @@
+openapi: 3.0.0
+info:
+  description: myDesc
+  title: myTitle
+  version: 1.0.0
+paths:
+  /widgets:
+    post:
+      operationId: widgetCreate
+      requestBody:
+        content:
+          application/json:
+            schema:
+              type: object
+              properties:
+                prop1:
+                  type: string
+                prop2:
+                  type: string
+                prop3:
+                  type: string
+              required:
+                - prop1
+                - prop2
+                - prop3
+      responses:
+        '200':
+          description: successful operation
+          content:
+            application/json:
+              schema:
+                type: object
+                properties:
+                  prop1:
+                    type: string
+                  prop2:
+                    type: string
+                  prop3:
+                    type: string
+                required:
+                  - prop1
+                  - prop2
diff --git a/core/src/test/resources/bc_request_schema_format_decreased.yaml b/core/src/test/resources/bc_request_schema_format_decreased.yaml
new file mode 100644
index 000000000..3a69caf83
--- /dev/null
+++ b/core/src/test/resources/bc_request_schema_format_decreased.yaml
@@ -0,0 +1,115 @@
+openapi: 3.0.0
+info:
+  description: myDesc
+  title: myTitle
+  version: 1.0.0
+paths:
+  /widgets:
+    post:
+      operationId: widgetCreate
+      requestBody:
+        content:
+          application/json:
+            schema:
+              $ref: '#/components/schemas/WidgetCreateRequest'
+      responses:
+        '200':
+          description: successful operation
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/WidgetCreateResponse'
+    put:
+      operationId: widgetUpdate
+      requestBody:
+        content:
+          application/json:
+            schema:
+              type: object
+              properties:
+                put_prop1:
+                  type: string
+                put_prop2:
+                  type: string
+      responses:
+        '200':
+          description: successful operation
+          content:
+            application/json:
+              schema:
+                type: string
+components:
+  schemas:
+    WidgetCreateRequest:
+      type: object
+      properties:
+        to_create:
+          $ref: '#/components/schemas/Widget_Polymorphic'
+        request_prop1:
+          type: integer
+          format: int32
+        request_prop2:
+          type: integer
+          format: int32
+      required:
+        - to_create
+        - request_prop1
+    WidgetCreateResponse:
+      type: object
+      properties:
+        created:
+          $ref: '#/components/schemas/Widget_Polymorphic'
+        response_prop1:
+          type: integer
+          format: int32
+        response_prop2:
+          type: integer
+          format: int64
+      required:
+        - created
+        - response_prop1
+    Widget_Polymorphic:
+      type: object
+      oneOf:
+        - $ref: '#/components/schemas/Doodad'
+        - $ref: '#/components/schemas/Gadget'
+      discriminator:
+        propertyName: '@type'
+    Widget:
+      type: object
+      properties:
+        '@type':
+          type: string
+        prop1:
+          type: string
+        prop2:
+          type: integer
+          format: int32
+          deprecated: true
+      required:
+        - '@type'
+        - prop1
+    Doodad:
+      type: object
+      allOf:
+        - $ref: '#/components/schemas/Widget'
+        - type: object
+          properties:
+            doodad_prop1:
+              type: string
+    Gadget:
+      type: object
+      allOf:
+        - $ref: '#/components/schemas/Widget'
+        - type: object
+          properties:
+            gadget_prop1:
+              type: string
+    Gizmo:
+      type: object
+      allOf:
+        - $ref: '#/components/schemas/Widget'
+        - type: object
+          properties:
+            gizmo_prop1:
+              type: string
diff --git a/core/src/test/resources/bc_request_schema_format_increased.yaml b/core/src/test/resources/bc_request_schema_format_increased.yaml
new file mode 100644
index 000000000..6201e14c9
--- /dev/null
+++ b/core/src/test/resources/bc_request_schema_format_increased.yaml
@@ -0,0 +1,115 @@
+openapi: 3.0.0
+info:
+  description: myDesc
+  title: myTitle
+  version: 1.0.0
+paths:
+  /widgets:
+    post:
+      operationId: widgetCreate
+      requestBody:
+        content:
+          application/json:
+            schema:
+              $ref: '#/components/schemas/WidgetCreateRequest'
+      responses:
+        '200':
+          description: successful operation
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/WidgetCreateResponse'
+    put:
+      operationId: widgetUpdate
+      requestBody:
+        content:
+          application/json:
+            schema:
+              type: object
+              properties:
+                put_prop1:
+                  type: string
+                put_prop2:
+                  type: string
+      responses:
+        '200':
+          description: successful operation
+          content:
+            application/json:
+              schema:
+                type: string
+components:
+  schemas:
+    WidgetCreateRequest:
+      type: object
+      properties:
+        to_create:
+          $ref: '#/components/schemas/Widget_Polymorphic'
+        request_prop1:
+          type: integer
+          format: int64
+        request_prop2:
+          type: integer
+          format: int64
+      required:
+        - to_create
+        - request_prop1
+    WidgetCreateResponse:
+      type: object
+      properties:
+        created:
+          $ref: '#/components/schemas/Widget_Polymorphic'
+        response_prop1:
+          type: integer
+          format: int32
+        response_prop2:
+          type: integer
+          format: int64
+      required:
+        - created
+        - response_prop1
+    Widget_Polymorphic:
+      type: object
+      oneOf:
+        - $ref: '#/components/schemas/Doodad'
+        - $ref: '#/components/schemas/Gadget'
+      discriminator:
+        propertyName: '@type'
+    Widget:
+      type: object
+      properties:
+        '@type':
+          type: string
+        prop1:
+          type: string
+        prop2:
+          type: integer
+          format: int32
+          deprecated: true
+      required:
+        - '@type'
+        - prop1
+    Doodad:
+      type: object
+      allOf:
+        - $ref: '#/components/schemas/Widget'
+        - type: object
+          properties:
+            doodad_prop1:
+              type: string
+    Gadget:
+      type: object
+      allOf:
+        - $ref: '#/components/schemas/Widget'
+        - type: object
+          properties:
+            gadget_prop1:
+              type: string
+    Gizmo:
+      type: object
+      allOf:
+        - $ref: '#/components/schemas/Widget'
+        - type: object
+          properties:
+            gizmo_prop1:
+              type: string
diff --git a/core/src/test/resources/bc_request_schema_props_put_increased.yaml b/core/src/test/resources/bc_request_schema_props_put_increased.yaml
new file mode 100644
index 000000000..9ca619a47
--- /dev/null
+++ b/core/src/test/resources/bc_request_schema_props_put_increased.yaml
@@ -0,0 +1,117 @@
+openapi: 3.0.0
+info:
+  description: myDesc
+  title: myTitle
+  version: 1.0.0
+paths:
+  /widgets:
+    post:
+      operationId: widgetCreate
+      requestBody:
+        content:
+          application/json:
+            schema:
+              $ref: '#/components/schemas/WidgetCreateRequest'
+      responses:
+        '200':
+          description: successful operation
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/WidgetCreateResponse'
+    put:
+      operationId: widgetUpdate
+      requestBody:
+        content:
+          application/json:
+            schema:
+              type: object
+              properties:
+                put_prop1:
+                  type: string
+                put_prop2:
+                  type: string
+                put_prop3:
+                  type: string
+      responses:
+        '200':
+          description: successful operation
+          content:
+            application/json:
+              schema:
+                type: string
+components:
+  schemas:
+    WidgetCreateRequest:
+      type: object
+      properties:
+        to_create:
+          $ref: '#/components/schemas/Widget_Polymorphic'
+        request_prop1:
+          type: integer
+          format: int32
+        request_prop2:
+          type: integer
+          format: int64
+      required:
+        - to_create
+        - request_prop1
+    WidgetCreateResponse:
+      type: object
+      properties:
+        created:
+          $ref: '#/components/schemas/Widget_Polymorphic'
+        response_prop1:
+          type: integer
+          format: int32
+        response_prop2:
+          type: integer
+          format: int64
+      required:
+        - created
+        - response_prop1
+    Widget_Polymorphic:
+      type: object
+      oneOf:
+        - $ref: '#/components/schemas/Doodad'
+        - $ref: '#/components/schemas/Gadget'
+      discriminator:
+        propertyName: '@type'
+    Widget:
+      type: object
+      properties:
+        '@type':
+          type: string
+        prop1:
+          type: string
+        prop2:
+          type: integer
+          format: int32
+          deprecated: true
+      required:
+        - '@type'
+        - prop1
+    Doodad:
+      type: object
+      allOf:
+        - $ref: '#/components/schemas/Widget'
+        - type: object
+          properties:
+            doodad_prop1:
+              type: string
+    Gadget:
+      type: object
+      allOf:
+        - $ref: '#/components/schemas/Widget'
+        - type: object
+          properties:
+            gadget_prop1:
+              type: string
+    Gizmo:
+      type: object
+      allOf:
+        - $ref: '#/components/schemas/Widget'
+        - type: object
+          properties:
+            gizmo_prop1:
+              type: string
diff --git a/core/src/test/resources/bc_required_base.yaml b/core/src/test/resources/bc_required_base.yaml
new file mode 100644
index 000000000..711cd20a0
--- /dev/null
+++ b/core/src/test/resources/bc_required_base.yaml
@@ -0,0 +1,41 @@
+openapi: 3.0.0
+info:
+  description: myDesc
+  title: myTitle
+  version: 1.0.0
+paths:
+  /widgets:
+    post:
+      operationId: widgetCreate
+      requestBody:
+        content:
+          application/json:
+            schema:
+              type: object
+              properties:
+                prop1:
+                  type: string
+                prop2:
+                  type: string
+                prop3:
+                  type: string
+              required:
+                - prop1
+                - prop2
+      responses:
+        '200':
+          description: successful operation
+          content:
+            application/json:
+              schema:
+                type: object
+                properties:
+                  prop1:
+                    type: string
+                  prop2:
+                    type: string
+                  prop3:
+                    type: string
+                required:
+                  - prop1
+                  - prop2
diff --git a/core/src/test/resources/bc_required_changed_but_compatible.yaml b/core/src/test/resources/bc_required_changed_but_compatible.yaml
new file mode 100644
index 000000000..4e352896f
--- /dev/null
+++ b/core/src/test/resources/bc_required_changed_but_compatible.yaml
@@ -0,0 +1,41 @@
+openapi: 3.0.0
+info:
+  description: myDesc
+  title: myTitle
+  version: 1.0.0
+paths:
+  /widgets:
+    post:
+      operationId: widgetCreate
+      requestBody:
+        content:
+          application/json:
+            schema:
+              type: object
+              properties:
+                prop1:
+                  type: string
+                prop2:
+                  type: string
+                prop3:
+                  type: string
+              required:
+                - prop1
+      responses:
+        '200':
+          description: successful operation
+          content:
+            application/json:
+              schema:
+                type: object
+                properties:
+                  prop1:
+                    type: string
+                  prop2:
+                    type: string
+                  prop3:
+                    type: string
+                required:
+                  - prop1
+                  - prop2
+                  - prop3
diff --git a/core/src/test/resources/bc_response_apiresponse_base.yaml b/core/src/test/resources/bc_response_apiresponse_base.yaml
new file mode 100644
index 000000000..640a7dc1a
--- /dev/null
+++ b/core/src/test/resources/bc_response_apiresponse_base.yaml
@@ -0,0 +1,18 @@
+openapi: 3.0.0
+info:
+  description: myDesc
+  title: myTitle
+  version: 1.0.0
+paths:
+  /widgets:
+    get:
+      operationId: listWidgets
+      responses:
+        '200':
+          description: successful operation
+        '400':
+          description: Invalid status value
+  /widgets/index:
+    post:
+      operationId: indexWidgets
+      description: Regression test for https://github.com/OpenAPITools/openapi-diff/pull/206 (handle missing responses)
diff --git a/core/src/test/resources/bc_response_apiresponse_changed_but_compatible.yaml b/core/src/test/resources/bc_response_apiresponse_changed_but_compatible.yaml
new file mode 100644
index 000000000..6ad621348
--- /dev/null
+++ b/core/src/test/resources/bc_response_apiresponse_changed_but_compatible.yaml
@@ -0,0 +1,22 @@
+openapi: 3.0.0
+info:
+  description: myDesc
+  title: myTitle
+  version: 1.0.0
+paths:
+  /widgets:
+    get:
+      operationId: listWidgets
+      responses:
+        '200':
+          description: successful operation
+        '201':
+          description: OK
+        '400':
+          description: Invalid status value
+  /widgets/index:
+    post:
+      operationId: indexWidgets
+      responses:
+        '200':
+          description: successful operation
diff --git a/core/src/test/resources/bc_response_apiresponse_decreased.yaml b/core/src/test/resources/bc_response_apiresponse_decreased.yaml
new file mode 100644
index 000000000..6502cf0ce
--- /dev/null
+++ b/core/src/test/resources/bc_response_apiresponse_decreased.yaml
@@ -0,0 +1,16 @@
+openapi: 3.0.0
+info:
+  description: myDesc
+  title: myTitle
+  version: 1.0.0
+paths:
+  /widgets:
+    get:
+      operationId: listWidgets
+      responses:
+        '200':
+          description: successful operation
+  /widgets/index:
+    post:
+      operationId: indexWidgets
+      description: Regression test for https://github.com/OpenAPITools/openapi-diff/pull/206 (handle missing responses)
diff --git a/core/src/test/resources/bc_response_content_decreased.yaml b/core/src/test/resources/bc_response_content_decreased.yaml
new file mode 100644
index 000000000..2915db537
--- /dev/null
+++ b/core/src/test/resources/bc_response_content_decreased.yaml
@@ -0,0 +1,26 @@
+openapi: 3.0.0
+info:
+  description: myDesc
+  title: myTitle
+  version: 1.0.0
+paths:
+  /widgets:
+    post:
+      operationId: widgetCreate
+      requestBody:
+        content:
+          application/json:
+            schema:
+              type: integer
+              format: int32
+          application/text:
+            schema:
+              type: string
+      responses:
+        '200':
+          description: successful operation
+          content:
+            application/json:
+              schema:
+                type: integer
+                format: int32
diff --git a/core/src/test/resources/bc_response_enum_increased.yaml b/core/src/test/resources/bc_response_enum_increased.yaml
new file mode 100644
index 000000000..27a0d58d0
--- /dev/null
+++ b/core/src/test/resources/bc_response_enum_increased.yaml
@@ -0,0 +1,34 @@
+openapi: 3.0.0
+info:
+  description: myDesc
+  title: myTitle
+  version: 1.0.0
+paths:
+  /widgets:
+    get:
+      operationId: listWidgets
+      parameters:
+        - name: param-inline-enum
+          in: query
+          required: true
+          schema:
+            type: string
+            enum:
+              - param-inline-enum-val-1
+              - param-inline-enum-val-2
+            default: param-inline-enum-val-1
+      responses:
+        '200':
+          description: successful operation
+          content:
+            application/json:
+              schema:
+                type: object
+                properties:
+                  enum-prop:
+                    type: string
+                    enum:
+                      - enum-prop-val-1
+                      - enum-prop-val-2
+                      - enum-prop-val-3
+                    default: enum-prop-val-1
diff --git a/core/src/test/resources/bc_response_header_base.yaml b/core/src/test/resources/bc_response_header_base.yaml
new file mode 100644
index 000000000..1680fee21
--- /dev/null
+++ b/core/src/test/resources/bc_response_header_base.yaml
@@ -0,0 +1,29 @@
+openapi: 3.0.0
+info:
+  description: myDesc
+  title: myTitle
+  version: 1.0.0
+paths:
+  /widgets:
+    get:
+      operationId: listWidgets
+      responses:
+        '200':
+          description: successful operation
+          headers:
+            X-Header-1:
+              required: true
+              schema:
+                type: integer
+                format: int32
+            X-Header-2:
+              schema:
+                type: integer
+            X-Header-3:
+              schema:
+                type: integer
+              explode: false
+            X-Header-4:
+              schema:
+                type: integer
+              explode: true
diff --git a/core/src/test/resources/bc_response_header_changed_but_compatible.yaml b/core/src/test/resources/bc_response_header_changed_but_compatible.yaml
new file mode 100644
index 000000000..b660903db
--- /dev/null
+++ b/core/src/test/resources/bc_response_header_changed_but_compatible.yaml
@@ -0,0 +1,31 @@
+openapi: 3.0.0
+info:
+  description: myDesc
+  title: myTitle
+  version: 1.0.0
+paths:
+  /widgets:
+    get:
+      operationId: listWidgets
+      responses:
+        '200':
+          description: successful operation
+          headers:
+            X-Header-1:
+              required: true
+              schema:
+                type: integer
+                format: int32
+              deprecated: false
+            X-Header-2:
+              schema:
+                type: integer
+              deprecated: true
+            X-Header-3:
+              schema:
+                type: integer
+              explode: false
+            X-Header-4:
+              schema:
+                type: integer
+              explode: true
diff --git a/core/src/test/resources/bc_response_header_explode_changed.yaml b/core/src/test/resources/bc_response_header_explode_changed.yaml
new file mode 100644
index 000000000..3403c28eb
--- /dev/null
+++ b/core/src/test/resources/bc_response_header_explode_changed.yaml
@@ -0,0 +1,29 @@
+openapi: 3.0.0
+info:
+  description: myDesc
+  title: myTitle
+  version: 1.0.0
+paths:
+  /widgets:
+    get:
+      operationId: listWidgets
+      responses:
+        '200':
+          description: successful operation
+          headers:
+            X-Header-1:
+              required: true
+              schema:
+                type: integer
+                format: int32
+            X-Header-2:
+              schema:
+                type: integer
+              explode: true
+            X-Header-3:
+              schema:
+                type: integer
+            X-Header-4:
+              schema:
+                type: integer
+              explode: false
diff --git a/core/src/test/resources/bc_response_header_required_decreased.yaml b/core/src/test/resources/bc_response_header_required_decreased.yaml
new file mode 100644
index 000000000..9b457cdc7
--- /dev/null
+++ b/core/src/test/resources/bc_response_header_required_decreased.yaml
@@ -0,0 +1,28 @@
+openapi: 3.0.0
+info:
+  description: myDesc
+  title: myTitle
+  version: 1.0.0
+paths:
+  /widgets:
+    get:
+      operationId: listWidgets
+      responses:
+        '200':
+          description: successful operation
+          headers:
+            X-Header-1:
+              schema:
+                type: integer
+                format: int32
+            X-Header-2:
+              schema:
+                type: integer
+            X-Header-3:
+              schema:
+                type: integer
+              explode: false
+            X-Header-4:
+              schema:
+                type: integer
+              explode: true
diff --git a/core/src/test/resources/bc_response_header_required_increased.yaml b/core/src/test/resources/bc_response_header_required_increased.yaml
new file mode 100644
index 000000000..7d76aec7e
--- /dev/null
+++ b/core/src/test/resources/bc_response_header_required_increased.yaml
@@ -0,0 +1,30 @@
+openapi: 3.0.0
+info:
+  description: myDesc
+  title: myTitle
+  version: 1.0.0
+paths:
+  /widgets:
+    get:
+      operationId: listWidgets
+      responses:
+        '200':
+          description: successful operation
+          headers:
+            X-Header-1:
+              required: true
+              schema:
+                type: integer
+                format: int32
+            X-Header-2:
+              schema:
+                type: integer
+              required: true
+            X-Header-3:
+              schema:
+                type: integer
+              explode: false
+            X-Header-4:
+              schema:
+                type: integer
+              explode: true
diff --git a/core/src/test/resources/bc_response_headers_base.yaml b/core/src/test/resources/bc_response_headers_base.yaml
new file mode 100644
index 000000000..17716ba06
--- /dev/null
+++ b/core/src/test/resources/bc_response_headers_base.yaml
@@ -0,0 +1,21 @@
+openapi: 3.0.0
+info:
+  description: myDesc
+  title: myTitle
+  version: 1.0.0
+paths:
+  /widgets:
+    get:
+      operationId: listWidgets
+      responses:
+        '200':
+          description: successful operation
+          headers:
+            X-Header-1:
+              required: true
+              schema:
+                type: integer
+                format: int32
+            X-Header-2:
+              schema:
+                type: integer
diff --git a/core/src/test/resources/bc_response_headers_changed_but_compatible.yaml b/core/src/test/resources/bc_response_headers_changed_but_compatible.yaml
new file mode 100644
index 000000000..837b3e222
--- /dev/null
+++ b/core/src/test/resources/bc_response_headers_changed_but_compatible.yaml
@@ -0,0 +1,24 @@
+openapi: 3.0.0
+info:
+  description: myDesc
+  title: myTitle
+  version: 1.0.0
+paths:
+  /widgets:
+    get:
+      operationId: listWidgets
+      responses:
+        '200':
+          description: successful operation
+          headers:
+            X-Header-1:
+              required: true
+              schema:
+                type: integer
+                format: int32
+            X-Header-2:
+              schema:
+                type: integer
+            X-Header-3:
+              schema:
+                type: integer
diff --git a/core/src/test/resources/bc_response_headers_decreased.yaml b/core/src/test/resources/bc_response_headers_decreased.yaml
new file mode 100644
index 000000000..c5166c9c2
--- /dev/null
+++ b/core/src/test/resources/bc_response_headers_decreased.yaml
@@ -0,0 +1,18 @@
+openapi: 3.0.0
+info:
+  description: myDesc
+  title: myTitle
+  version: 1.0.0
+paths:
+  /widgets:
+    get:
+      operationId: listWidgets
+      responses:
+        '200':
+          description: successful operation
+          headers:
+            X-Header-1:
+              required: true
+              schema:
+                type: integer
+                format: int32
diff --git a/core/src/test/resources/bc_response_maxlength_increased.yaml b/core/src/test/resources/bc_response_maxlength_increased.yaml
new file mode 100644
index 000000000..40dd4309d
--- /dev/null
+++ b/core/src/test/resources/bc_response_maxlength_increased.yaml
@@ -0,0 +1,30 @@
+openapi: 3.0.0
+info:
+  description: myDesc
+  title: myTitle
+  version: 1.0.0
+paths:
+  /widgets:
+    post:
+      operationId: widgetCreate
+      requestBody:
+        content:
+          application/json:
+            schema:
+              type: string
+              maxLength: 16
+          application/xml:
+            schema:
+              type: string
+      responses:
+        '200':
+          description: successful operation
+          content:
+            application/json:
+              schema:
+                type: string
+                maxLength: 20
+            application/xml:
+              schema:
+                type: string
+                maxLength: 20
diff --git a/core/src/test/resources/bc_response_numericrange_exclusive_max_deleted.yaml b/core/src/test/resources/bc_response_numericrange_exclusive_max_deleted.yaml
new file mode 100644
index 000000000..385b4759d
--- /dev/null
+++ b/core/src/test/resources/bc_response_numericrange_exclusive_max_deleted.yaml
@@ -0,0 +1,54 @@
+openapi: 3.0.0
+info:
+  description: myDesc
+  title: myTitle
+  version: 1.0.0
+paths:
+  /widgets:
+    post:
+      operationId: widgetCreate
+      requestBody:
+        content:
+          application/json:
+            schema:
+              type: integer
+              format: int32
+              exclusiveMinimum: false
+              exclusiveMaximum: false
+          application/text:
+            schema:
+              type: integer
+              format: int32
+              minimum: 10
+              maximum: 20
+          application/xml:
+            schema:
+              type: integer
+              format: int32
+              minimum: 10
+              maximum: 20
+              exclusiveMinimum: true
+              exclusiveMaximum: true
+      responses:
+        '200':
+          description: successful operation
+          content:
+            application/json:
+              schema:
+                type: integer
+                format: int32
+                exclusiveMinimum: false
+                exclusiveMaximum: false
+            application/text:
+              schema:
+                type: integer
+                format: int32
+                minimum: 10
+                maximum: 20
+            application/xml:
+              schema:
+                type: integer
+                format: int32
+                minimum: 10
+                maximum: 20
+                exclusiveMinimum: true
diff --git a/core/src/test/resources/bc_response_numericrange_exclusive_max_unset.yaml b/core/src/test/resources/bc_response_numericrange_exclusive_max_unset.yaml
new file mode 100644
index 000000000..e6c1448cd
--- /dev/null
+++ b/core/src/test/resources/bc_response_numericrange_exclusive_max_unset.yaml
@@ -0,0 +1,55 @@
+openapi: 3.0.0
+info:
+  description: myDesc
+  title: myTitle
+  version: 1.0.0
+paths:
+  /widgets:
+    post:
+      operationId: widgetCreate
+      requestBody:
+        content:
+          application/json:
+            schema:
+              type: integer
+              format: int32
+              exclusiveMinimum: false
+              exclusiveMaximum: false
+          application/text:
+            schema:
+              type: integer
+              format: int32
+              minimum: 10
+              maximum: 20
+          application/xml:
+            schema:
+              type: integer
+              format: int32
+              minimum: 10
+              maximum: 20
+              exclusiveMinimum: true
+              exclusiveMaximum: true
+      responses:
+        '200':
+          description: successful operation
+          content:
+            application/json:
+              schema:
+                type: integer
+                format: int32
+                exclusiveMinimum: false
+                exclusiveMaximum: false
+            application/text:
+              schema:
+                type: integer
+                format: int32
+                minimum: 10
+                maximum: 20
+            application/xml:
+              schema:
+                type: integer
+                format: int32
+                minimum: 10
+                maximum: 20
+                exclusiveMinimum: true
+                exclusiveMaximum: false
diff --git a/core/src/test/resources/bc_response_numericrange_exclusive_min_deleted.yaml b/core/src/test/resources/bc_response_numericrange_exclusive_min_deleted.yaml
new file mode 100644
index 000000000..49881a7f4
--- /dev/null
+++ b/core/src/test/resources/bc_response_numericrange_exclusive_min_deleted.yaml
@@ -0,0 +1,54 @@
+openapi: 3.0.0
+info:
+  description: myDesc
+  title: myTitle
+  version: 1.0.0
+paths:
+  /widgets:
+    post:
+      operationId: widgetCreate
+      requestBody:
+        content:
+          application/json:
+            schema:
+              type: integer
+              format: int32
+              exclusiveMinimum: false
+              exclusiveMaximum: false
+          application/text:
+            schema:
+              type: integer
+              format: int32
+              minimum: 10
+              maximum: 20
+          application/xml:
+            schema:
+              type: integer
+              format: int32
+              minimum: 10
+              maximum: 20
+              exclusiveMinimum: true
+              exclusiveMaximum: true
+      responses:
+        '200':
+          description: successful operation
+          content:
+            application/json:
+              schema:
+                type: integer
+                format: int32
+                exclusiveMinimum: false
+                exclusiveMaximum: false
+            application/text:
+              schema:
+                type: integer
+                format: int32
+                minimum: 10
+                maximum: 20
+            application/xml:
+              schema:
+                type: integer
+                format: int32
+                minimum: 10
+                maximum: 20
+                exclusiveMaximum: true
diff --git a/core/src/test/resources/bc_response_numericrange_exclusive_min_unset.yaml b/core/src/test/resources/bc_response_numericrange_exclusive_min_unset.yaml
new file mode 100644
index 000000000..75bb758fa
--- /dev/null
+++ b/core/src/test/resources/bc_response_numericrange_exclusive_min_unset.yaml
@@ -0,0 +1,55 @@
+openapi: 3.0.0
+info:
+  description: myDesc
+  title: myTitle
+  version: 1.0.0
+paths:
+  /widgets:
+    post:
+      operationId: widgetCreate
+      requestBody:
+        content:
+          application/json:
+            schema:
+              type: integer
+              format: int32
+              exclusiveMinimum: false
+              exclusiveMaximum: false
+          application/text:
+            schema:
+              type: integer
+              format: int32
+              minimum: 10
+              maximum: 20
+          application/xml:
+            schema:
+              type: integer
+              format: int32
+              minimum: 10
+              maximum: 20
+              exclusiveMinimum: true
+              exclusiveMaximum: true
+      responses:
+        '200':
+          description: successful operation
+          content:
+            application/json:
+              schema:
+                type: integer
+                format: int32
+                exclusiveMinimum: false
+                exclusiveMaximum: false
+            application/text:
+              schema:
+                type: integer
+                format: int32
+                minimum: 10
+                maximum: 20
+            application/xml:
+              schema:
+                type: integer
+                format: int32
+                minimum: 10
+                maximum: 20
+                exclusiveMinimum: false
+                exclusiveMaximum: true
diff --git a/core/src/test/resources/bc_response_numericrange_max_deleted.yaml b/core/src/test/resources/bc_response_numericrange_max_deleted.yaml
new file mode 100644
index 000000000..6edf439f2
--- /dev/null
+++ b/core/src/test/resources/bc_response_numericrange_max_deleted.yaml
@@ -0,0 +1,53 @@
+openapi: 3.0.0
+info:
+  description: myDesc
+  title: myTitle
+  version: 1.0.0
+paths:
+  /widgets:
+    post:
+      operationId: widgetCreate
+      requestBody:
+        content:
+          application/json:
+            schema:
+              type: integer
+              format: int32
+              exclusiveMinimum: false
+              exclusiveMaximum: false
+          application/text:
+            schema:
+              type: integer
+              format: int32
+              minimum: 10
+              maximum: 20
+          application/xml:
+            schema:
+              type: integer
+              format: int32
+              minimum: 10
+              maximum: 20
+              exclusiveMinimum: true
+              exclusiveMaximum: true
+      responses:
+        '200':
+          description: successful operation
+          content:
+            application/json:
+              schema:
+                type: integer
+                format: int32
+                exclusiveMinimum: false
+                exclusiveMaximum: false
+            application/text:
+              schema:
+                type: integer
+                format: int32
+                minimum: 10
+            application/xml:
+              schema:
+                type: integer
+                format: int32
+                minimum: 10
+                exclusiveMinimum: true
+                exclusiveMaximum: true
diff --git a/core/src/test/resources/bc_response_numericrange_max_increased.yaml b/core/src/test/resources/bc_response_numericrange_max_increased.yaml
new file mode 100644
index 000000000..e994bf88c
--- /dev/null
+++ b/core/src/test/resources/bc_response_numericrange_max_increased.yaml
@@ -0,0 +1,55 @@
+openapi: 3.0.0
+info:
+  description: myDesc
+  title: myTitle
+  version: 1.0.0
+paths:
+  /widgets:
+    post:
+      operationId: widgetCreate
+      requestBody:
+        content:
+          application/json:
+            schema:
+              type: integer
+              format: int32
+              exclusiveMinimum: false
+              exclusiveMaximum: false
+          application/text:
+            schema:
+              type: integer
+              format: int32
+              minimum: 10
+              maximum: 20
+          application/xml:
+            schema:
+              type: integer
+              format: int32
+              minimum: 10
+              maximum: 20
+              exclusiveMinimum: true
+              exclusiveMaximum: true
+      responses:
+        '200':
+          description: successful operation
+          content:
+            application/json:
+              schema:
+                type: integer
+                format: int32
+                exclusiveMinimum: false
+                exclusiveMaximum: false
+            application/text:
+              schema:
+                type: integer
+                format: int32
+                minimum: 10
+                maximum: 21
+            application/xml:
+              schema:
+                type: integer
+                format: int32
+                minimum: 10
+                maximum: 21
+                exclusiveMinimum: true
+                exclusiveMaximum: true
diff --git a/core/src/test/resources/bc_response_numericrange_min_decreased.yaml b/core/src/test/resources/bc_response_numericrange_min_decreased.yaml
new file mode 100644
index 000000000..2055a3247
--- /dev/null
+++ b/core/src/test/resources/bc_response_numericrange_min_decreased.yaml
@@ -0,0 +1,55 @@
+openapi: 3.0.0
+info:
+  description: myDesc
+  title: myTitle
+  version: 1.0.0
+paths:
+  /widgets:
+    post:
+      operationId: widgetCreate
+      requestBody:
+        content:
+          application/json:
+            schema:
+              type: integer
+              format: int32
+              exclusiveMinimum: false
+              exclusiveMaximum: false
+          application/text:
+            schema:
+              type: integer
+              format: int32
+              minimum: 10
+              maximum: 20
+          application/xml:
+            schema:
+              type: integer
+              format: int32
+              minimum: 10
+              maximum: 20
+              exclusiveMinimum: true
+              exclusiveMaximum: true
+      responses:
+        '200':
+          description: successful operation
+          content:
+            application/json:
+              schema:
+                type: integer
+                format: int32
+                exclusiveMinimum: false
+                exclusiveMaximum: false
+            application/text:
+              schema:
+                type: integer
+                format: int32
+                minimum: 9
+                maximum: 20
+            application/xml:
+              schema:
+                type: integer
+                format: int32
+                minimum: 9
+                maximum: 20
+                exclusiveMinimum: true
+                exclusiveMaximum: true
diff --git a/core/src/test/resources/bc_response_numericrange_min_deleted.yaml b/core/src/test/resources/bc_response_numericrange_min_deleted.yaml
new file mode 100644
index 000000000..087245c07
--- /dev/null
+++ b/core/src/test/resources/bc_response_numericrange_min_deleted.yaml
@@ -0,0 +1,53 @@
+openapi: 3.0.0
+info:
+  description: myDesc
+  title: myTitle
+  version: 1.0.0
+paths:
+  /widgets:
+    post:
+      operationId: widgetCreate
+      requestBody:
+        content:
+          application/json:
+            schema:
+              type: integer
+              format: int32
+              exclusiveMinimum: false
+              exclusiveMaximum: false
+          application/text:
+            schema:
+              type: integer
+              format: int32
+              minimum: 10
+              maximum: 20
+          application/xml:
+            schema:
+              type: integer
+              format: int32
+              minimum: 10
+              maximum: 20
+              exclusiveMinimum: true
+              exclusiveMaximum: true
+      responses:
+        '200':
+          description: successful operation
+          content:
+            application/json:
+              schema:
+                type: integer
+                format: int32
+                exclusiveMinimum: false
+                exclusiveMaximum: false
+            application/text:
+              schema:
+                type: integer
+                format: int32
+                maximum: 20
+            application/xml:
+              schema:
+                type: integer
+                format: int32
+                maximum: 20
+                exclusiveMinimum: true
+                exclusiveMaximum: true
diff --git a/core/src/test/resources/bc_response_oneof_increased.yaml b/core/src/test/resources/bc_response_oneof_increased.yaml
new file mode 100644
index 000000000..0d6636e0e
--- /dev/null
+++ b/core/src/test/resources/bc_response_oneof_increased.yaml
@@ -0,0 +1,49 @@
+openapi: 3.0.0
+info:
+  description: myDesc
+  title: myTitle
+  version: 1.0.0
+paths:
+  /widgets:
+    post:
+      operationId: widgetCreate
+      requestBody:
+        content:
+          application/json:
+            schema:
+              $ref: '#/components/schemas/WidgetCreateRequest'
+      responses:
+        '200':
+          description: successful operation
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/WidgetCreateResponse'
+components:
+  schemas:
+    WidgetCreateRequest:
+      type: object
+      oneOf:
+        - $ref: '#/components/schemas/Doodad'
+        - $ref: '#/components/schemas/Gadget'
+    WidgetCreateResponse:
+      type: object
+      oneOf:
+        - $ref: '#/components/schemas/Doodad'
+        - $ref: '#/components/schemas/Gadget'
+        - $ref: '#/components/schemas/Gizmo'
+    Doodad:
+      type: object
+      properties:
+        doodad_prop1:
+          type: string
+    Gadget:
+      type: object
+      properties:
+        gadget_prop1:
+          type: string
+    Gizmo:
+      type: object
+      properties:
+        gizmo_prop1:
+          type: string
diff --git a/core/src/test/resources/bc_response_required_decreased.yaml b/core/src/test/resources/bc_response_required_decreased.yaml
new file mode 100644
index 000000000..e77d73da4
--- /dev/null
+++ b/core/src/test/resources/bc_response_required_decreased.yaml
@@ -0,0 +1,40 @@
+openapi: 3.0.0
+info:
+  description: myDesc
+  title: myTitle
+  version: 1.0.0
+paths:
+  /widgets:
+    post:
+      operationId: widgetCreate
+      requestBody:
+        content:
+          application/json:
+            schema:
+              type: object
+              properties:
+                prop1:
+                  type: string
+                prop2:
+                  type: string
+                prop3:
+                  type: string
+              required:
+                - prop1
+                - prop2
+      responses:
+        '200':
+          description: successful operation
+          content:
+            application/json:
+              schema:
+                type: object
+                properties:
+                  prop1:
+                    type: string
+                  prop2:
+                    type: string
+                  prop3:
+                    type: string
+                required:
+                  - prop1
diff --git a/core/src/test/resources/bc_response_schema_format_decreased.yaml b/core/src/test/resources/bc_response_schema_format_decreased.yaml
new file mode 100644
index 000000000..66ad527df
--- /dev/null
+++ b/core/src/test/resources/bc_response_schema_format_decreased.yaml
@@ -0,0 +1,115 @@
+openapi: 3.0.0
+info:
+  description: myDesc
+  title: myTitle
+  version: 1.0.0
+paths:
+  /widgets:
+    post:
+      operationId: widgetCreate
+      requestBody:
+        content:
+          application/json:
+            schema:
+              $ref: '#/components/schemas/WidgetCreateRequest'
+      responses:
+        '200':
+          description: successful operation
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/WidgetCreateResponse'
+    put:
+      operationId: widgetUpdate
+      requestBody:
+        content:
+          application/json:
+            schema:
+              type: object
+              properties:
+                put_prop1:
+                  type: string
+                put_prop2:
+                  type: string
+      responses:
+        '200':
+          description: successful operation
+          content:
+            application/json:
+              schema:
+                type: string
+components:
+  schemas:
+    WidgetCreateRequest:
+      type: object
+      properties:
+        to_create:
+          $ref: '#/components/schemas/Widget_Polymorphic'
+        request_prop1:
+          type: integer
+          format: int32
+        request_prop2:
+          type: integer
+          format: int64
+      required:
+        - to_create
+        - request_prop1
+    WidgetCreateResponse:
+      type: object
+      properties:
+        created:
+          $ref: '#/components/schemas/Widget_Polymorphic'
+        response_prop1:
+          type: integer
+          format: int32
+        response_prop2:
+          type: integer
+          format: int32
+      required:
+        - created
+        - response_prop1
+    Widget_Polymorphic:
+      type: object
+      oneOf:
+        - $ref: '#/components/schemas/Doodad'
+        - $ref: '#/components/schemas/Gadget'
+      discriminator:
+        propertyName: '@type'
+    Widget:
+      type: object
+      properties:
+        '@type':
+          type: string
+        prop1:
+          type: string
+        prop2:
+          type: integer
+          format: int32
+          deprecated: true
+      required:
+        - '@type'
+        - prop1
+    Doodad:
+      type: object
+      allOf:
+        - $ref: '#/components/schemas/Widget'
+        - type: object
+          properties:
+            doodad_prop1:
+              type: string
+    Gadget:
+      type: object
+      allOf:
+        - $ref: '#/components/schemas/Widget'
+        - type: object
+          properties:
+            gadget_prop1:
+              type: string
+    Gizmo:
+      type: object
+      allOf:
+        - $ref: '#/components/schemas/Widget'
+        - type: object
+          properties:
+            gizmo_prop1:
+              type: string
diff --git a/core/src/test/resources/bc_response_schema_format_increased.yaml b/core/src/test/resources/bc_response_schema_format_increased.yaml
new file mode 100644
index 000000000..1ecc86fb6
--- /dev/null
+++ b/core/src/test/resources/bc_response_schema_format_increased.yaml
@@ -0,0 +1,115 @@
+openapi: 3.0.0
+info:
+  description: myDesc
+  title: myTitle
+  version: 1.0.0
+paths:
+  /widgets:
+    post:
+      operationId: widgetCreate
+      requestBody:
+        content:
+          application/json:
+            schema:
+              $ref: '#/components/schemas/WidgetCreateRequest'
+      responses:
+        '200':
+          description: successful operation
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/WidgetCreateResponse'
+    put:
+      operationId: widgetUpdate
+      requestBody:
+        content:
+          application/json:
+            schema:
+              type: object
+              properties:
+                put_prop1:
+                  type: string
+                put_prop2:
+                  type: string
+      responses:
+        '200':
+          description: successful operation
+          content:
+            application/json:
+              schema:
+                type: string
+components:
+  schemas:
+    WidgetCreateRequest:
+      type: object
+      properties:
+        to_create:
+          $ref: '#/components/schemas/Widget_Polymorphic'
+        request_prop1:
+          type: integer
+          format: int32
+        request_prop2:
+          type: integer
+          format: int64
+      required:
+        - to_create
+        - request_prop1
+    WidgetCreateResponse:
+      type: object
+      properties:
+        created:
+          $ref: '#/components/schemas/Widget_Polymorphic'
+        response_prop1:
+          type: integer
+          format: int64
+        response_prop2:
+          type: integer
+          format: int64
+      required:
+        - created
+        - response_prop1
+    Widget_Polymorphic:
+      type: object
+      oneOf:
+        - $ref: '#/components/schemas/Doodad'
+        - $ref: '#/components/schemas/Gadget'
+      discriminator:
+        propertyName: '@type'
+    Widget:
+      type: object
+      properties:
+        '@type':
+          type: string
+        prop1:
+          type: string
+        prop2:
+          type: integer
+          format: int32
+          deprecated: true
+      required:
+        - '@type'
+        - prop1
+    Doodad:
+      type: object
+      allOf:
+        - $ref: '#/components/schemas/Widget'
+        - type: object
+          properties:
+            doodad_prop1:
+              type: string
+    Gadget:
+      type: object
+      allOf:
+        - $ref: '#/components/schemas/Widget'
+        - type: object
+          properties:
+            gadget_prop1:
+              type: string
+    Gizmo:
+      type: object
+      allOf:
+        - $ref: '#/components/schemas/Widget'
+        - type: object
+          properties:
+            gizmo_prop1:
+              type: string
diff --git a/core/src/test/resources/bc_response_schema_props_required_decreased.yaml b/core/src/test/resources/bc_response_schema_props_required_decreased.yaml
new file mode 100644
index 000000000..cbd2c5a44
--- /dev/null
+++ b/core/src/test/resources/bc_response_schema_props_required_decreased.yaml
@@ -0,0 +1,112 @@
+openapi: 3.0.0
+info:
+  description: myDesc
+  title: myTitle
+  version: 1.0.0
+paths:
+  /widgets:
+    post:
+      operationId: widgetCreate
+      requestBody:
+        content:
+          application/json:
+            schema:
+              $ref: '#/components/schemas/WidgetCreateRequest'
+      responses:
+        '200':
+          description: successful operation
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/WidgetCreateResponse'
+    put:
+      operationId: widgetUpdate
+      requestBody:
+        content:
+          application/json:
+            schema:
+              type: object
+              properties:
+                put_prop1:
+                  type: string
+                put_prop2:
+                  type: string
+      responses:
+        '200':
+          description: successful operation
+          content:
+            application/json:
+              schema:
+                type: string
+components:
+  schemas:
+    WidgetCreateRequest:
+      type: object
+      properties:
+        to_create:
+          $ref: '#/components/schemas/Widget_Polymorphic'
+        request_prop1:
+          type: integer
+          format: int32
+        request_prop2:
+          type: integer
+          format: int64
+      required:
+        - to_create
+        - request_prop1
+    WidgetCreateResponse:
+      type: object
+      properties:
+        created:
+          $ref: '#/components/schemas/Widget_Polymorphic'
+        response_prop2:
+          type: integer
+          format: int64
+      required:
+        - created
+        - response_prop1
+    Widget_Polymorphic:
+      type: object
+      oneOf:
+        - $ref: '#/components/schemas/Doodad'
+        - $ref: '#/components/schemas/Gadget'
+      discriminator:
+        propertyName: '@type'
+    Widget:
+      type: object
+      properties:
+        '@type':
+          type: string
+        prop1:
+          type: string
+        prop2:
+          type: integer
+          format: int32
+          deprecated: true
+      required:
+        - '@type'
+        - prop1
+    Doodad:
+      type: object
+      allOf:
+        - $ref: '#/components/schemas/Widget'
+        - type: object
+          properties:
+            doodad_prop1:
+              type: string
+    Gadget:
+      type: object
+      allOf:
+        - $ref: '#/components/schemas/Widget'
+        - type: object
+          properties:
+            gadget_prop1:
+              type: string
+    Gizmo:
+      type: object
+      allOf:
+        - $ref: '#/components/schemas/Widget'
+        - type: object
+          properties:
+            gizmo_prop1:
+              type: string
diff --git a/core/src/test/resources/bc_response_writeonly_increased.yaml b/core/src/test/resources/bc_response_writeonly_increased.yaml
new file mode 100644
index 000000000..a009b8dc6
--- /dev/null
+++ b/core/src/test/resources/bc_response_writeonly_increased.yaml
@@ -0,0 +1,34 @@
+openapi: 3.0.0
+info:
+  description: myDesc
+  title: myTitle
+  version: 1.0.0
+paths:
+  /widgets:
+    post:
+      operationId: widgetCreate
+      requestBody:
+        content:
+          application/json:
+            schema:
+              type: object
+              properties:
+                prop1:
+                  type: string
+                  writeOnly: true
+      responses:
+        '200':
+          description: successful operation
+          content:
+            application/json:
+              schema:
+                type: object
+                properties:
+                  prop1:
+                    type: string
+                    writeOnly: true
+                  prop2:
+                    type: string
+                    writeOnly: true
+                required:
+                  - prop1
diff --git a/core/src/test/resources/bc_response_writeonly_required_decreased.yaml b/core/src/test/resources/bc_response_writeonly_required_decreased.yaml
new file mode 100644
index 000000000..74bf153ef
--- /dev/null
+++ b/core/src/test/resources/bc_response_writeonly_required_decreased.yaml
@@ -0,0 +1,32 @@
+openapi: 3.0.0
+info:
+  description: myDesc
+  title: myTitle
+  version: 1.0.0
+paths:
+  /widgets:
+    post:
+      operationId: widgetCreate
+      requestBody:
+        content:
+          application/json:
+            schema:
+              type: object
+              properties:
+                prop1:
+                  type: string
+                  writeOnly: true
+      responses:
+        '200':
+          description: successful operation
+          content:
+            application/json:
+              schema:
+                type: object
+                properties:
+                  prop1:
+                    type: string
+                  prop2:
+                    type: string
+                required:
+                  - prop1
diff --git a/core/src/test/resources/bc_schema_base.yaml b/core/src/test/resources/bc_schema_base.yaml
new file mode 100644
index 000000000..28d8800d8
--- /dev/null
+++ b/core/src/test/resources/bc_schema_base.yaml
@@ -0,0 +1,115 @@
+openapi: 3.0.0
+info:
+  description: myDesc
+  title: myTitle
+  version: 1.0.0
+paths:
+  /widgets:
+    post:
+      operationId: widgetCreate
+      requestBody:
+        content:
+          application/json:
+            schema:
+              $ref: '#/components/schemas/WidgetCreateRequest'
+      responses:
+        '200':
+          description: successful operation
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/WidgetCreateResponse'
+    put:
+      operationId: widgetUpdate
+      requestBody:
+        content:
+          application/json:
+            schema:
+              type: object
+              properties:
+                put_prop1:
+                  type: string
+                put_prop2:
+                  type: string
+      responses:
+        '200':
+          description: successful operation
+          content:
+            application/json:
+              schema:
+                type: string
+components:
+  schemas:
+    WidgetCreateRequest:
+      type: object
+      properties:
+        to_create:
+          $ref: '#/components/schemas/Widget_Polymorphic'
+        request_prop1:
+          type: integer
+          format: int32
+        request_prop2:
+          type: integer
+          format: int64
+      required:
+        - to_create
+        - request_prop1
+    WidgetCreateResponse:
+      type: object
+      properties:
+        created:
+          $ref: '#/components/schemas/Widget_Polymorphic'
+        response_prop1:
+          type: integer
+          format: int32
+        response_prop2:
+          type: integer
+          format: int64
+      required:
+        - created
+        - response_prop1
+    Widget_Polymorphic:
+      type: object
+      oneOf:
+        - $ref: '#/components/schemas/Doodad'
+        - $ref: '#/components/schemas/Gadget'
+      discriminator:
+        propertyName: '@type'
+    Widget:
+      type: object
+      properties:
+        '@type':
+          type: string
+        prop1:
+          type: string
+        prop2:
+          type: integer
+          format: int32
+          deprecated: true
+      required:
+        - '@type'
+        - prop1
+    Doodad:
+      type: object
+      allOf:
+        - $ref: '#/components/schemas/Widget'
+        - type: object
+          properties:
+            doodad_prop1:
+              type: string
+    Gadget:
+      type: object
+      allOf:
+        - $ref: '#/components/schemas/Widget'
+        - type: object
+          properties:
+            gadget_prop1:
+              type: string
+    Gizmo:
+      type: object
+      allOf:
+        - $ref: '#/components/schemas/Widget'
+        - type: object
+          properties:
+            gizmo_prop1:
+              type: string
diff --git a/core/src/test/resources/bc_schema_changed_but_compatible.yaml b/core/src/test/resources/bc_schema_changed_but_compatible.yaml
new file mode 100644
index 000000000..e8a6e7e94
--- /dev/null
+++ b/core/src/test/resources/bc_schema_changed_but_compatible.yaml
@@ -0,0 +1,114 @@
+openapi: 3.0.0
+info:
+  description: myDesc
+  title: myTitle
+  version: 1.0.0
+paths:
+  /widgets:
+    post:
+      operationId: widgetCreate
+      requestBody:
+        content:
+          application/json:
+            schema:
+              $ref: '#/components/schemas/WidgetCreateRequest'
+      responses:
+        '200':
+          description: successful operation
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/WidgetCreateResponse'
+    put:
+      operationId: widgetUpdate
+      requestBody:
+        content:
+          application/json:
+            schema:
+              type: object
+              properties:
+                put_prop1:
+                  type: string
+                put_prop2:
+                  type: string
+      responses:
+        '200':
+          description: successful operation
+          content:
+            application/json:
+              schema:
+                type: string
+components:
+  schemas:
+    WidgetCreateRequest:
+      type: object
+      properties:
+        to_create:
+          $ref: '#/components/schemas/Widget_Polymorphic'
+        request_prop2:
+          type: integer
+          format: int64
+        request_prop3:
+          type: integer
+          format: int64
+      required:
+        - to_create
+        - request_prop1
+    WidgetCreateResponse:
+      type: object
+      properties:
+        created:
+          $ref: '#/components/schemas/Widget_Polymorphic'
+        response_prop1:
+          type: integer
+          format: int32
+        response_prop3:
+          type: string
+      required:
+        - created
+        - response_prop1
+    Widget_Polymorphic:
+      type: object
+      oneOf:
+        - $ref: '#/components/schemas/Doodad'
+        - $ref: '#/components/schemas/Gadget'
+      discriminator:
+        propertyName: '@type'
+    Widget:
+      type: object
+      properties:
+        '@type':
+          type: string
+        prop1:
+          type: string
+          deprecated: true
+        prop2:
+          type: integer
+          format: int32
+      required:
+        - '@type'
+        - prop1
+    Doodad:
+      type: object
+      allOf:
+        - $ref: '#/components/schemas/Widget'
+        - type: object
+          properties:
+            doodad_prop1:
+              type: string
+    Gadget:
+      type: object
+      allOf:
+        - $ref: '#/components/schemas/Widget'
+        - type: object
+          properties:
+            gadget_prop1:
+              type: string
+    Gizmo:
+      type: object
+      allOf:
+        - $ref: '#/components/schemas/Widget'
+        - type: object
+          properties:
+            gizmo_prop1:
+              type: string
diff --git a/core/src/test/resources/bc_schema_discriminator_changed.yaml b/core/src/test/resources/bc_schema_discriminator_changed.yaml
new file mode 100644
index 000000000..fd9569001
--- /dev/null
+++ b/core/src/test/resources/bc_schema_discriminator_changed.yaml
@@ -0,0 +1,115 @@
+openapi: 3.0.0
+info:
+  description: myDesc
+  title: myTitle
+  version: 1.0.0
+paths:
+  /widgets:
+    post:
+      operationId: widgetCreate
+      requestBody:
+        content:
+          application/json:
+            schema:
+              $ref: '#/components/schemas/WidgetCreateRequest'
+      responses:
+        '200':
+          description: successful operation
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/WidgetCreateResponse'
+    put:
+      operationId: widgetUpdate
+      requestBody:
+        content:
+          application/json:
+            schema:
+              type: object
+              properties:
+                put_prop1:
+                  type: string
+                put_prop2:
+                  type: string
+      responses:
+        '200':
+          description: successful operation
+          content:
+            application/json:
+              schema:
+                type: string
+components:
+  schemas:
+    WidgetCreateRequest:
+      type: object
+      properties:
+        to_create:
+          $ref: '#/components/schemas/Widget_Polymorphic'
+        request_prop1:
+          type: integer
+          format: int32
+        request_prop2:
+          type: integer
+          format: int64
+      required:
+        - to_create
+        - request_prop1
+    WidgetCreateResponse:
+      type: object
+      properties:
+        created:
+          $ref: '#/components/schemas/Widget_Polymorphic'
+        response_prop1:
+          type: integer
+          format: int32
+        response_prop2:
+          type: integer
+          format: int64
+      required:
+        - created
+        - response_prop1
+    Widget_Polymorphic:
+      type: object
+      oneOf:
+        - $ref: '#/components/schemas/Doodad'
+        - $ref: '#/components/schemas/Gadget'
+      discriminator:
+        propertyName: 'prop1'
+    Widget:
+      type: object
+      properties:
+        '@type':
+          type: string
+        prop1:
+          type: string
+        prop2:
+          type: integer
+          format: int32
+          deprecated: true
+      required:
+        - '@type'
+        - prop1
+    Doodad:
+      type: object
+      allOf:
+        - $ref: '#/components/schemas/Widget'
+        - type: object
+          properties:
+            doodad_prop1:
+              type: string
+    Gadget:
+      type: object
+      allOf:
+        - $ref: '#/components/schemas/Widget'
+        - type: object
+          properties:
+            gadget_prop1:
+              type: string
+    Gizmo:
+      type: object
+      allOf:
+        - $ref: '#/components/schemas/Widget'
+        - type: object
+          properties:
+            gizmo_prop1:
+              type: string
diff --git a/core/src/test/resources/bc_schema_type_changed.yaml b/core/src/test/resources/bc_schema_type_changed.yaml
new file mode 100644
index 000000000..946d16890
--- /dev/null
+++ b/core/src/test/resources/bc_schema_type_changed.yaml
@@ -0,0 +1,114 @@
+openapi: 3.0.0
+info:
+  description: myDesc
+  title: myTitle
+  version: 1.0.0
+paths:
+  /widgets:
+    post:
+      operationId: widgetCreate
+      requestBody:
+        content:
+          application/json:
+            schema:
+              $ref: '#/components/schemas/WidgetCreateRequest'
+      responses:
+        '200':
+          description: successful operation
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/WidgetCreateResponse'
+    put:
+      operationId: widgetUpdate
+      requestBody:
+        content:
+          application/json:
+            schema:
+              type: object
+              properties:
+                put_prop1:
+                  type: string
+                put_prop2:
+                  type: string
+      responses:
+        '200':
+          description: successful operation
+          content:
+            application/json:
+              schema:
+                type: string
+components:
+  schemas:
+    WidgetCreateRequest:
+      type: object
+      properties:
+        to_create:
+          $ref: '#/components/schemas/Widget_Polymorphic'
+        request_prop1:
+          type: integer
+          format: int32
+        request_prop2:
+          type: integer
+          format: int64
+      required:
+        - to_create
+        - request_prop1
+    WidgetCreateResponse:
+      type: object
+      properties:
+        created:
+          $ref: '#/components/schemas/Widget_Polymorphic'
+        response_prop1:
+          type: string
+        response_prop2:
+          type: integer
+          format: int64
+      required:
+        - created
+        - response_prop1
+    Widget_Polymorphic:
+      type: object
+      oneOf:
+        - $ref: '#/components/schemas/Doodad'
+        - $ref: '#/components/schemas/Gadget'
+      discriminator:
+        propertyName: '@type'
+    Widget:
+      type: object
+      properties:
+        '@type':
+          type: string
+        prop1:
+          type: string
+        prop2:
+          type: integer
+          format: int32
+          deprecated: true
+      required:
+        - '@type'
+        - prop1
+    Doodad:
+      type: object
+      allOf:
+        - $ref: '#/components/schemas/Widget'
+        - type: object
+          properties:
+            doodad_prop1:
+              type: string
+    Gadget:
+      type: object
+      allOf:
+        - $ref: '#/components/schemas/Widget'
+        - type: object
+          properties:
+            gadget_prop1:
+              type: string
+    Gizmo:
+      type: object
+      allOf:
+        - $ref: '#/components/schemas/Widget'
+        - type: object
+          properties:
+            gizmo_prop1:
+              type: string
diff --git a/core/src/test/resources/bc_security_requirement_base.yaml b/core/src/test/resources/bc_security_requirement_base.yaml
new file mode 100644
index 000000000..477736519
--- /dev/null
+++ b/core/src/test/resources/bc_security_requirement_base.yaml
@@ -0,0 +1,30 @@
+openapi: 3.0.0
+info:
+  description: myDesc
+  title: myTitle
+  version: 1.0.0
+paths:
+  /widgets:
+    get:
+      operationId: listWidgets
+      responses:
+        '200':
+          description: successful operation
+          content:
+            application/json:
+              schema:
+                type: string
+      security:
+        - my-scheme-1: []
+          my-scheme-2: []
+components:
+  securitySchemes:
+    my-scheme-1:
+      type: http
+      scheme: basic
+    my-scheme-2:
+      type: http
+      scheme: basic
+    my-scheme-3:
+      type: http
+      scheme: basic
diff --git a/core/src/test/resources/bc_security_requirement_changed_but_compatible.yaml b/core/src/test/resources/bc_security_requirement_changed_but_compatible.yaml
new file mode 100644
index 000000000..9a58c327f
--- /dev/null
+++ b/core/src/test/resources/bc_security_requirement_changed_but_compatible.yaml
@@ -0,0 +1,29 @@
+openapi: 3.0.0
+info:
+  description: myDesc
+  title: myTitle
+  version: 1.0.0
+paths:
+  /widgets:
+    get:
+      operationId: listWidgets
+      responses:
+        '200':
+          description: successful operation
+          content:
+            application/json:
+              schema:
+                type: string
+      security:
+        - my-scheme-1: []
+components:
+  securitySchemes:
+    my-scheme-1:
+      type: http
+      scheme: basic
+    my-scheme-2:
+      type: http
+      scheme: basic
+    my-scheme-3:
+      type: http
+      scheme: basic
diff --git a/core/src/test/resources/bc_security_requirement_schemes_increased.yaml b/core/src/test/resources/bc_security_requirement_schemes_increased.yaml
new file mode 100644
index 000000000..234aa7a29
--- /dev/null
+++ b/core/src/test/resources/bc_security_requirement_schemes_increased.yaml
@@ -0,0 +1,31 @@
+openapi: 3.0.0
+info:
+  description: myDesc
+  title: myTitle
+  version: 1.0.0
+paths:
+  /widgets:
+    get:
+      operationId: listWidgets
+      responses:
+        '200':
+          description: successful operation
+          content:
+            application/json:
+              schema:
+                type: string
+      security:
+        - my-scheme-1: []
+          my-scheme-2: []
+          my-scheme-3: []
+components:
+  securitySchemes:
+    my-scheme-1:
+      type: http
+      scheme: basic
+    my-scheme-2:
+      type: http
+      scheme: basic
+    my-scheme-3:
+      type: http
+      scheme: basic
diff --git a/core/src/test/resources/bc_security_requirements_base.yaml b/core/src/test/resources/bc_security_requirements_base.yaml
new file mode 100644
index 000000000..9c4c04650
--- /dev/null
+++ b/core/src/test/resources/bc_security_requirements_base.yaml
@@ -0,0 +1,30 @@
+openapi: 3.0.0
+info:
+  description: myDesc
+  title: myTitle
+  version: 1.0.0
+paths:
+  /widgets:
+    get:
+      operationId: listWidgets
+      responses:
+        '200':
+          description: successful operation
+          content:
+            application/json:
+              schema:
+                type: string
+      security:
+        - my-scheme-1: []
+        - my-scheme-2: []
+components:
+  securitySchemes:
+    my-scheme-1:
+      type: http
+      scheme: basic
+    my-scheme-2:
+      type: http
+      scheme: basic
+    my-scheme-3:
+      type: http
+      scheme: basic
diff --git a/core/src/test/resources/bc_security_requirements_changed_but_compatible.yaml b/core/src/test/resources/bc_security_requirements_changed_but_compatible.yaml
new file mode 100644
index 000000000..ba983bc36
--- /dev/null
+++ b/core/src/test/resources/bc_security_requirements_changed_but_compatible.yaml
@@ -0,0 +1,31 @@
+openapi: 3.0.0
+info:
+  description: myDesc
+  title: myTitle
+  version: 1.0.0
+paths:
+  /widgets:
+    get:
+      operationId: listWidgets
+      responses:
+        '200':
+          description: successful operation
+          content:
+            application/json:
+              schema:
+                type: string
+      security:
+        - my-scheme-1: []
+        - my-scheme-2: []
+        - my-scheme-3: []
+components:
+  securitySchemes:
+    my-scheme-1:
+      type: http
+      scheme: basic
+    my-scheme-2:
+      type: http
+      scheme: basic
+    my-scheme-3:
+      type: http
+      scheme: basic
diff --git a/core/src/test/resources/bc_security_requirements_decreased.yaml b/core/src/test/resources/bc_security_requirements_decreased.yaml
new file mode 100644
index 000000000..9a58c327f
--- /dev/null
+++ b/core/src/test/resources/bc_security_requirements_decreased.yaml
@@ -0,0 +1,29 @@
+openapi: 3.0.0
+info:
+  description: myDesc
+  title: myTitle
+  version: 1.0.0
+paths:
+  /widgets:
+    get:
+      operationId: listWidgets
+      responses:
+        '200':
+          description: successful operation
+          content:
+            application/json:
+              schema:
+                type: string
+      security:
+        - my-scheme-1: []
+components:
+  securitySchemes:
+    my-scheme-1:
+      type: http
+      scheme: basic
+    my-scheme-2:
+      type: http
+      scheme: basic
+    my-scheme-3:
+      type: http
+      scheme: basic
diff --git a/core/src/test/resources/bc_security_requirements_scheme_type_changed.yaml b/core/src/test/resources/bc_security_requirements_scheme_type_changed.yaml
new file mode 100644
index 000000000..d2e6f1a95
--- /dev/null
+++ b/core/src/test/resources/bc_security_requirements_scheme_type_changed.yaml
@@ -0,0 +1,31 @@
+openapi: 3.0.0
+info:
+  description: myDesc
+  title: myTitle
+  version: 1.0.0
+paths:
+  /widgets:
+    get:
+      operationId: listWidgets
+      responses:
+        '200':
+          description: successful operation
+          content:
+            application/json:
+              schema:
+                type: string
+      security:
+        - my-scheme-1: []
+        - my-scheme-2: []
+components:
+  securitySchemes:
+    my-scheme-1:
+      type: apiKey
+      name: api_key
+      in: header
+    my-scheme-2:
+      type: http
+      scheme: basic
+    my-scheme-3:
+      type: http
+      scheme: basic
diff --git a/core/src/test/resources/bc_security_scheme_base.yaml b/core/src/test/resources/bc_security_scheme_base.yaml
new file mode 100644
index 000000000..00caad137
--- /dev/null
+++ b/core/src/test/resources/bc_security_scheme_base.yaml
@@ -0,0 +1,42 @@
+openapi: 3.0.0
+info:
+  description: myDesc
+  title: myTitle
+  version: 1.0.0
+paths:
+  /widgets:
+    get:
+      operationId: listWidgets
+      responses:
+        '200':
+          description: successful operation
+          content:
+            application/json:
+              schema:
+                type: string
+      security:
+        - basic-scheme-1: []
+        - apikey-scheme-1: []
+        - openidconnect-scheme-1: []
+        - oauth2-scheme-1:
+            - "scope1"
+            - "scope2"
+components:
+  securitySchemes:
+    apikey-scheme-1:
+      type: apiKey
+      name: api_key
+      in: header
+    basic-scheme-1:
+      type: http
+    openidconnect-scheme-1:
+      type: openIdConnect
+      openIdConnectUrl: https://example.com/api/openidconnect
+    oauth2-scheme-1:
+      type: oauth2
+      flows:
+        implicit:
+          authorizationUrl: https://example.com/api/oauth/dialog
+          scopes:
+            scope1: scope1 description
+            scope2: scope2 description
diff --git a/core/src/test/resources/bc_security_scheme_bearer_format_changed.yaml b/core/src/test/resources/bc_security_scheme_bearer_format_changed.yaml
new file mode 100644
index 000000000..bf7fcca56
--- /dev/null
+++ b/core/src/test/resources/bc_security_scheme_bearer_format_changed.yaml
@@ -0,0 +1,43 @@
+openapi: 3.0.0
+info:
+  description: myDesc
+  title: myTitle
+  version: 1.0.0
+paths:
+  /widgets:
+    get:
+      operationId: listWidgets
+      responses:
+        '200':
+          description: successful operation
+          content:
+            application/json:
+              schema:
+                type: string
+      security:
+        - basic-scheme-1: []
+        - apikey-scheme-1: []
+        - openidconnect-scheme-1: []
+        - oauth2-scheme-1:
+            - "scope1"
+            - "scope2"
+components:
+  securitySchemes:
+    apikey-scheme-1:
+      type: apiKey
+      name: api_key
+      in: header
+    basic-scheme-1:
+      type: http
+      bearerFormat: myBearerFormat
+    openidconnect-scheme-1:
+      type: openIdConnect
+      openIdConnectUrl: https://example.com/api/openidconnect
+    oauth2-scheme-1:
+      type: oauth2
+      flows:
+        implicit:
+          authorizationUrl: https://example.com/api/oauth/dialog
+          scopes:
+            scope1: scope1 description
+            scope2: scope2 description
diff --git a/core/src/test/resources/bc_security_scheme_changed_but_compatible.yaml b/core/src/test/resources/bc_security_scheme_changed_but_compatible.yaml
new file mode 100644
index 000000000..5cf1f26bf
--- /dev/null
+++ b/core/src/test/resources/bc_security_scheme_changed_but_compatible.yaml
@@ -0,0 +1,41 @@
+openapi: 3.0.0
+info:
+  description: myDesc
+  title: myTitle
+  version: 1.0.0
+paths:
+  /widgets:
+    get:
+      operationId: listWidgets
+      responses:
+        '200':
+          description: successful operation
+          content:
+            application/json:
+              schema:
+                type: string
+      security:
+        - basic-scheme-1: []
+        - apikey-scheme-1: []
+        - openidconnect-scheme-1: []
+        - oauth2-scheme-1:
+            - "scope1"
+components:
+  securitySchemes:
+    apikey-scheme-1:
+      type: apiKey
+      name: api_key
+      in: header
+    basic-scheme-1:
+      type: http
+    openidconnect-scheme-1:
+      type: openIdConnect
+      openIdConnectUrl: https://example.com/api/openidconnect
+    oauth2-scheme-1:
+      type: oauth2
+      flows:
+        implicit:
+          authorizationUrl: https://example.com/api/oauth/dialog
+          scopes:
+            scope1: scope1 description
+            scope2: scope2 description
diff --git a/core/src/test/resources/bc_security_scheme_in_changed.yaml b/core/src/test/resources/bc_security_scheme_in_changed.yaml
new file mode 100644
index 000000000..5d937c361
--- /dev/null
+++ b/core/src/test/resources/bc_security_scheme_in_changed.yaml
@@ -0,0 +1,42 @@
+openapi: 3.0.0
+info:
+  description: myDesc
+  title: myTitle
+  version: 1.0.0
+paths:
+  /widgets:
+    get:
+      operationId: listWidgets
+      responses:
+        '200':
+          description: successful operation
+          content:
+            application/json:
+              schema:
+                type: string
+      security:
+        - basic-scheme-1: []
+        - apikey-scheme-1: []
+        - openidconnect-scheme-1: []
+        - oauth2-scheme-1:
+            - "scope1"
+            - "scope2"
+components:
+  securitySchemes:
+    apikey-scheme-1:
+      type: apiKey
+      name: api_key
+      in: query
+    basic-scheme-1:
+      type: http
+    openidconnect-scheme-1:
+      type: openIdConnect
+      openIdConnectUrl: https://example.com/api/openidconnect
+    oauth2-scheme-1:
+      type: oauth2
+      flows:
+        implicit:
+          authorizationUrl: https://example.com/api/oauth/dialog
+          scopes:
+            scope1: scope1 description
+            scope2: scope2 description
diff --git a/core/src/test/resources/bc_security_scheme_open_id_connect_url_changed.yaml b/core/src/test/resources/bc_security_scheme_open_id_connect_url_changed.yaml
new file mode 100644
index 000000000..e981a945f
--- /dev/null
+++ b/core/src/test/resources/bc_security_scheme_open_id_connect_url_changed.yaml
@@ -0,0 +1,42 @@
+openapi: 3.0.0
+info:
+  description: myDesc
+  title: myTitle
+  version: 1.0.0
+paths:
+  /widgets:
+    get:
+      operationId: listWidgets
+      responses:
+        '200':
+          description: successful operation
+          content:
+            application/json:
+              schema:
+                type: string
+      security:
+        - basic-scheme-1: []
+        - apikey-scheme-1: []
+        - openidconnect-scheme-1: []
+        - oauth2-scheme-1:
+            - "scope1"
+            - "scope2"
+components:
+  securitySchemes:
+    apikey-scheme-1:
+      type: apiKey
+      name: api_key
+      in: header
+    basic-scheme-1:
+      type: http
+    openidconnect-scheme-1:
+      type: openIdConnect
+      openIdConnectUrl: https://example.com/api/openidconnect2
+    oauth2-scheme-1:
+      type: oauth2
+      flows:
+        implicit:
+          authorizationUrl: https://example.com/api/oauth/dialog
+          scopes:
+            scope1: scope1 description
+            scope2: scope2 description
diff --git a/core/src/test/resources/bc_security_scheme_scheme_changed.yaml b/core/src/test/resources/bc_security_scheme_scheme_changed.yaml
new file mode 100644
index 000000000..5b474049d
--- /dev/null
+++ b/core/src/test/resources/bc_security_scheme_scheme_changed.yaml
@@ -0,0 +1,43 @@
+openapi: 3.0.0
+info:
+  description: myDesc
+  title: myTitle
+  version: 1.0.0
+paths:
+  /widgets:
+    get:
+      operationId: listWidgets
+      responses:
+        '200':
+          description: successful operation
+          content:
+            application/json:
+              schema:
+                type: string
+      security:
+        - basic-scheme-1: []
+        - apikey-scheme-1: []
+        - openidconnect-scheme-1: []
+        - oauth2-scheme-1:
+            - "scope1"
+            - "scope2"
+components:
+  securitySchemes:
+    apikey-scheme-1:
+      type: apiKey
+      name: api_key
+      in: header
+    basic-scheme-1:
+      type: http
+      scheme: Digest
+    openidconnect-scheme-1:
+      type: openIdConnect
+      openIdConnectUrl: https://example.com/api/openidconnect
+    oauth2-scheme-1:
+      type: oauth2
+      flows:
+        implicit:
+          authorizationUrl: https://example.com/api/oauth/dialog
+          scopes:
+            scope1: scope1 description
+            scope2: scope2 description
diff --git a/core/src/test/resources/bc_security_scheme_scopes_increased.yaml b/core/src/test/resources/bc_security_scheme_scopes_increased.yaml
new file mode 100644
index 000000000..8cfe76833
--- /dev/null
+++ b/core/src/test/resources/bc_security_scheme_scopes_increased.yaml
@@ -0,0 +1,43 @@
+openapi: 3.0.0
+info:
+  description: myDesc
+  title: myTitle
+  version: 1.0.0
+paths:
+  /widgets:
+    get:
+      operationId: listWidgets
+      responses:
+        '200':
+          description: successful operation
+          content:
+            application/json:
+              schema:
+                type: string
+      security:
+        - basic-scheme-1: []
+        - apikey-scheme-1: []
+        - openidconnect-scheme-1: []
+        - oauth2-scheme-1:
+            - "scope1"
+            - "scope2"
+            - "scope3"
+components:
+  securitySchemes:
+    apikey-scheme-1:
+      type: apiKey
+      name: api_key
+      in: header
+    basic-scheme-1:
+      type: http
+    openidconnect-scheme-1:
+      type: openIdConnect
+      openIdConnectUrl: https://example.com/api/openidconnect
+    oauth2-scheme-1:
+      type: oauth2
+      flows:
+        implicit:
+          authorizationUrl: https://example.com/api/oauth/dialog
+          scopes:
+            scope1: scope1 description
+            scope2: scope2 description
diff --git a/core/src/test/resources/bc_security_scheme_type_changed.yaml b/core/src/test/resources/bc_security_scheme_type_changed.yaml
new file mode 100644
index 000000000..985a1a17b
--- /dev/null
+++ b/core/src/test/resources/bc_security_scheme_type_changed.yaml
@@ -0,0 +1,42 @@
+openapi: 3.0.0
+info:
+  description: myDesc
+  title: myTitle
+  version: 1.0.0
+paths:
+  /widgets:
+    get:
+      operationId: listWidgets
+      responses:
+        '200':
+          description: successful operation
+          content:
+            application/json:
+              schema:
+                type: string
+      security:
+        - basic-scheme-1: []
+        - apikey-scheme-1: []
+        - openidconnect-scheme-1: []
+        - oauth2-scheme-1:
+            - "scope1"
+            - "scope2"
+components:
+  securitySchemes:
+    apikey-scheme-1:
+      type: apiKey
+      name: api_key
+      in: header
+    basic-scheme-1:
+      type: apiKey
+    openidconnect-scheme-1:
+      type: openIdConnect
+      openIdConnectUrl: https://example.com/api/openidconnect
+    oauth2-scheme-1:
+      type: oauth2
+      flows:
+        implicit:
+          authorizationUrl: https://example.com/api/oauth/dialog
+          scopes:
+            scope1: scope1 description
+            scope2: scope2 description
diff --git a/core/src/test/resources/bc_writeonly_base.yaml b/core/src/test/resources/bc_writeonly_base.yaml
new file mode 100644
index 000000000..ad3c869db
--- /dev/null
+++ b/core/src/test/resources/bc_writeonly_base.yaml
@@ -0,0 +1,33 @@
+openapi: 3.0.0
+info:
+  description: myDesc
+  title: myTitle
+  version: 1.0.0
+paths:
+  /widgets:
+    post:
+      operationId: widgetCreate
+      requestBody:
+        content:
+          application/json:
+            schema:
+              type: object
+              properties:
+                prop1:
+                  type: string
+                  writeOnly: true
+      responses:
+        '200':
+          description: successful operation
+          content:
+            application/json:
+              schema:
+                type: object
+                properties:
+                  prop1:
+                    type: string
+                    writeOnly: true
+                  prop2:
+                    type: string
+                required:
+                  - prop1
diff --git a/core/src/test/resources/bc_writeonly_changed_but_compatible.yaml b/core/src/test/resources/bc_writeonly_changed_but_compatible.yaml
new file mode 100644
index 000000000..48857215b
--- /dev/null
+++ b/core/src/test/resources/bc_writeonly_changed_but_compatible.yaml
@@ -0,0 +1,32 @@
+openapi: 3.0.0
+info:
+  description: myDesc
+  title: myTitle
+  version: 1.0.0
+paths:
+  /widgets:
+    post:
+      operationId: widgetCreate
+      requestBody:
+        content:
+          application/json:
+            schema:
+              type: object
+              properties:
+                prop1:
+                  type: string
+      responses:
+        '200':
+          description: successful operation
+          content:
+            application/json:
+              schema:
+                type: object
+                properties:
+                  prop1:
+                    type: string
+                    writeOnly: true
+                  prop2:
+                    type: string
+                required:
+                  - prop1
diff --git a/core/src/test/resources/change_endpoint_1.yaml b/core/src/test/resources/change_endpoint_1.yaml
new file mode 100644
index 000000000..555015991
--- /dev/null
+++ b/core/src/test/resources/change_endpoint_1.yaml
@@ -0,0 +1,29 @@
+openapi: 3.0.0
+info:
+  description: myDesc
+  title: myTitle
+  version: 1.0.0
+paths:
+  /widgets:
+    get:
+      operationId: listWidgets
+      parameters:
+        - name: query-param-1
+          in: query
+          required: true
+          schema:
+            type: string
+        - name: query-param-2
+          in: query
+          style: form
+          explode: true
+          allowEmptyValue: true
+          schema:
+            type: string
+      responses:
+        '200':
+          description: successful operation
+          content:
+            application/json:
+              schema:
+                type: string
diff --git a/core/src/test/resources/change_endpoint_2.yaml b/core/src/test/resources/change_endpoint_2.yaml
new file mode 100644
index 000000000..281fd0514
--- /dev/null
+++ b/core/src/test/resources/change_endpoint_2.yaml
@@ -0,0 +1,30 @@
+openapi: 3.0.0
+info:
+  description: myDesc
+  title: myTitle
+  version: 1.0.0
+paths:
+  /widgets:
+    get:
+      operationId: listWidgets
+      parameters:
+        - name: query-param-1
+          in: query
+          allowEmptyValue: true
+          required: false
+          schema:
+            type: string
+        - name: query-param-2
+          in: query
+          style: form
+          explode: true
+          allowEmptyValue: true
+          schema:
+            type: string
+      responses:
+        '200':
+          description: successful operation
+          content:
+            application/json:
+              schema:
+                type: string
diff --git a/core/src/test/resources/delete_endpoint_1.yaml b/core/src/test/resources/delete_endpoint_1.yaml
new file mode 100644
index 000000000..59a315cd0
--- /dev/null
+++ b/core/src/test/resources/delete_endpoint_1.yaml
@@ -0,0 +1,52 @@
+openapi: 3.0.0
+servers:
+  - url: 'http://petstore.swagger.io/v2'
+info:
+  description: >-
+    This is a sample server Petstore server.  You can find out more about
+    Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net,
+    #swagger](http://swagger.io/irc/).  For this sample, you can use the api key
+    `special-key` to test the authorization filters.
+  version: 1.0.0
+  title: Swagger Petstore
+  termsOfService: 'http://swagger.io/terms/'
+  contact:
+    email: apiteam@swagger.io
+  license:
+    name: Apache 2.0
+    url: 'http://www.apache.org/licenses/LICENSE-2.0.html'
+paths:
+  /pet/{petId}:
+    get:
+      tags:
+        - pet
+      summary: gets a pet by id
+      description: ''
+      operationId: updatePetWithForm
+      parameters:
+        - name: petId
+          in: path
+          description: ID of pet that needs to be updated
+          required: true
+          schema:
+            type: integer
+      responses:
+        '405':
+          description: Invalid input
+  /pet/{petId2}:
+    get:
+      tags:
+        - pet
+      summary: gets a pet by id
+      description: ''
+      operationId: updatePetWithForm
+      parameters:
+        - name: petId2
+          in: path
+          description: ID of pet that needs to be updated
+          required: true
+          schema:
+            type: integer
+      responses:
+        '405':
+          description: Invalid input
diff --git a/core/src/test/resources/delete_endpoint_2.yaml b/core/src/test/resources/delete_endpoint_2.yaml
new file mode 100644
index 000000000..0c544c7b4
--- /dev/null
+++ b/core/src/test/resources/delete_endpoint_2.yaml
@@ -0,0 +1,35 @@
+openapi: 3.0.0
+servers:
+  - url: 'http://petstore.swagger.io/v2'
+info:
+  description: >-
+    This is a sample server Petstore server.  You can find out more about
+    Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net,
+    #swagger](http://swagger.io/irc/).  For this sample, you can use the api key
+    `special-key` to test the authorization filters.
+  version: 1.0.0
+  title: Swagger Petstore
+  termsOfService: 'http://swagger.io/terms/'
+  contact:
+    email: apiteam@swagger.io
+  license:
+    name: Apache 2.0
+    url: 'http://www.apache.org/licenses/LICENSE-2.0.html'
+paths:
+  /pet/{petId}:
+    get:
+      tags:
+        - pet
+      summary: gets a pet by id
+      description: ''
+      operationId: updatePetWithForm
+      parameters:
+        - name: petId
+          in: path
+          description: ID of pet that needs to be updated
+          required: true
+          schema:
+            type: integer
+      responses:
+        '405':
+          description: Invalid input
diff --git a/core/src/test/resources/deprecate_endpoint_1.yaml b/core/src/test/resources/deprecate_endpoint_1.yaml
new file mode 100644
index 000000000..6d08cb721
--- /dev/null
+++ b/core/src/test/resources/deprecate_endpoint_1.yaml
@@ -0,0 +1,52 @@
+openapi: 3.0.0
+servers:
+  - url: 'http://petstore.swagger.io/v2'
+info:
+  description: >-
+    This is a sample server Petstore server.  You can find out more about
+    Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net,
+    #swagger](http://swagger.io/irc/).  For this sample, you can use the api key
+    `special-key` to test the authorization filters.
+  version: 1.0.0
+  title: Swagger Petstore
+  termsOfService: 'http://swagger.io/terms/'
+  contact:
+    email: apiteam@swagger.io
+  license:
+    name: Apache 2.0
+    url: 'http://www.apache.org/licenses/LICENSE-2.0.html'
+paths:
+  /pet/{petId}:
+    get:
+      tags:
+        - pet
+      summary: gets a pet by id
+      description: ''
+      operationId: updatePetWithForm
+      parameters:
+        - name: petId
+          in: path
+          description: ID of pet that needs to be updated
+          required: true
+          schema:
+            type: integer
+      responses:
+        '405':
+          description: Invalid input
+  /pet/{petId2}:
+    post:
+      tags:
+        - pet
+      summary: deletes a pet
+      description: ''
+      operationId: deletePet
+      parameters:
+        - name: petId2
+          in: path
+          description: Pet ID to delete
+          required: true
+          schema:
+            type: integer
+      responses:
+        '405':
+          description: Invalid input
diff --git a/core/src/test/resources/deprecate_endpoint_2.yaml b/core/src/test/resources/deprecate_endpoint_2.yaml
new file mode 100644
index 000000000..95906047e
--- /dev/null
+++ b/core/src/test/resources/deprecate_endpoint_2.yaml
@@ -0,0 +1,53 @@
+openapi: 3.0.0
+servers:
+  - url: 'http://petstore.swagger.io/v2'
+info:
+  description: >-
+    This is a sample server Petstore server.  You can find out more about
+    Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net,
+    #swagger](http://swagger.io/irc/).  For this sample, you can use the api key
+    `special-key` to test the authorization filters.
+  version: 1.0.0
+  title: Swagger Petstore
+  termsOfService: 'http://swagger.io/terms/'
+  contact:
+    email: apiteam@swagger.io
+  license:
+    name: Apache 2.0
+    url: 'http://www.apache.org/licenses/LICENSE-2.0.html'
+paths:
+  /pet/{petId}:
+    get:
+      tags:
+        - pet
+      summary: gets a pet by id
+      description: ''
+      operationId: updatePetWithForm
+      parameters:
+        - name: petId
+          in: path
+          description: ID of pet that needs to be updated
+          required: true
+          schema:
+            type: integer
+      responses:
+        '405':
+          description: Invalid input
+  /pet/{petId2}:
+    post:
+      tags:
+        - pet
+      summary: deletes a pet
+      description: ''
+      operationId: deletePet
+      deprecated: true
+      parameters:
+        - name: petId2
+          in: path
+          description: Pet ID to delete
+          required: true
+          schema:
+            type: integer
+      responses:
+        '405':
+          description: Invalid input
diff --git a/core/src/test/resources/header_1.yaml b/core/src/test/resources/header_1.yaml
deleted file mode 100644
index 66f4a1665..000000000
--- a/core/src/test/resources/header_1.yaml
+++ /dev/null
@@ -1,129 +0,0 @@
-openapi: 3.0.0
-servers:
-  - url: 'http://petstore.swagger.io/v2'
-info:
-  description: >-
-    This is a sample server Petstore server.  You can find out more about
-    Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net,
-    #swagger](http://swagger.io/irc/).  For this sample, you can use the api key
-    `special-key` to test the authorization filters.
-  version: 1.0.0
-  title: Swagger Petstore
-  termsOfService: 'http://swagger.io/terms/'
-  contact:
-    email: apiteam@swagger.io
-  license:
-    name: Apache 2.0
-    url: 'http://www.apache.org/licenses/LICENSE-2.0.html'
-tags:
-  - name: pet
-    description: Everything about your Pets
-    externalDocs:
-      description: Find out more
-      url: 'http://swagger.io'
-  - name: store
-    description: Access to Petstore orders
-  - name: user
-    description: Operations about user
-    externalDocs:
-      description: Find out more about our store
-      url: 'http://swagger.io'
-paths:
-  /user/login:
-    get:
-      tags:
-        - user
-      summary: Logs user into the system
-      description: ''
-      operationId: loginUser
-      parameters:
-        - name: username
-          in: query
-          description: The user name for login
-          required: true
-          schema:
-            type: string
-      responses:
-        '200':
-          description: successful operation
-          headers:
-            X-Rate-Limit:
-              description: calls per hour allowed by the user
-              schema:
-                type: integer
-                format: int32
-            X-Expires-After:
-              description: date in UTC when token expires
-              schema:
-                type: integer
-          content:
-            application/xml:
-              schema:
-                type: integer
-            application/json:
-              schema:
-                type: string
-        '400':
-          description: Invalid username/password supplied
-externalDocs:
-  description: Find out more about Swagger
-  url: 'http://swagger.io'
-components:
-  requestBodies:
-    Pet:
-      content:
-        application/json:
-          schema:
-            $ref: '#/components/schemas/Pet'
-      description: Pet object that needs to be added to the store
-      required: true
-  securitySchemes:
-    petstore_auth:
-      type: oauth2
-      flows:
-        implicit:
-          authorizationUrl: 'http://petstore.swagger.io/oauth/dialog'
-          scopes:
-            'write:pets': modify pets in your account
-            'read:pets': read your pets
-    api_key:
-      type: apiKey
-      name: api_key
-      in: header
-  schemas:
-    Pet:
-      type: object
-      required:
-      - pet_type
-      properties:
-        pet_type:
-          type: string
-      discriminator:
-        propertyName: pet_type
-        mapping:
-          cachorro: Dog
-    Cat:
-      type: object
-      properties:
-        name:
-          type: string
-    Dog:
-      type: object
-      properties:
-        bark:
-          type: string
-    Lizard:
-      type: object
-      properties:
-        lovesRocks:
-          type: boolean
-
-    MyResponseType:
-      oneOf:
-      - $ref: '#/components/schemas/Cat'
-      - $ref: '#/components/schemas/Dog'
-      - $ref: '#/components/schemas/Lizard'
-      discriminator:
-        propertyName: pet_type
-        mapping:
-          dog: '#/components/schemas/Dog'
\ No newline at end of file
diff --git a/core/src/test/resources/header_2.yaml b/core/src/test/resources/header_2.yaml
deleted file mode 100644
index 124b75ef3..000000000
--- a/core/src/test/resources/header_2.yaml
+++ /dev/null
@@ -1,129 +0,0 @@
-openapi: 3.0.0
-servers:
-  - url: 'http://petstore.swagger.io/v2'
-info:
-  description: >-
-    This is a sample server Petstore server.  You can find out more about
-    Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net,
-    #swagger](http://swagger.io/irc/).  For this sample, you can use the api key
-    `special-key` to test the authorization filters.
-  version: 1.0.0
-  title: Swagger Petstore
-  termsOfService: 'http://swagger.io/terms/'
-  contact:
-    email: apiteam@swagger.io
-  license:
-    name: Apache 2.0
-    url: 'http://www.apache.org/licenses/LICENSE-2.0.html'
-tags:
-  - name: pet
-    description: Everything about your Pets
-    externalDocs:
-      description: Find out more
-      url: 'http://swagger.io'
-  - name: store
-    description: Access to Petstore orders
-  - name: user
-    description: Operations about user
-    externalDocs:
-      description: Find out more about our store
-      url: 'http://swagger.io'
-paths:
-  /user/login:
-    get:
-      tags:
-        - user
-      summary: Logs user into the system
-      description: ''
-      operationId: loginUser
-      parameters:
-        - name: username
-          in: query
-          description: The user name for login
-          required: true
-          schema:
-            type: string
-      responses:
-        '200':
-          description: successful operation
-          headers:
-            X-Rate-Limit-New:
-              description: calls per hour allowed by the user
-              schema:
-                type: integer
-                format: int32
-            X-Expires-After:
-              description: date in UTC when token expires
-              schema:
-                type: string
-          content:
-            application/xml:
-              schema:
-                type: integer
-            application/json:
-              schema:
-                type: string
-        '400':
-          description: Invalid username/password supplied
-externalDocs:
-  description: Find out more about Swagger
-  url: 'http://swagger.io'
-components:
-  requestBodies:
-    Pet:
-      content:
-        application/json:
-          schema:
-            $ref: '#/components/schemas/Pet'
-      description: Pet object that needs to be added to the store
-      required: true
-  securitySchemes:
-    petstore_auth:
-      type: oauth2
-      flows:
-        implicit:
-          authorizationUrl: 'http://petstore.swagger.io/oauth/dialog'
-          scopes:
-            'write:pets': modify pets in your account
-            'read:pets': read your pets
-    api_key:
-      type: apiKey
-      name: api_key
-      in: header
-  schemas:
-    Pet:
-      type: object
-      required:
-      - pet_type
-      properties:
-        pet_type:
-          type: string
-      discriminator:
-        propertyName: pet_type
-        mapping:
-          cachorro: Dog
-    Cat:
-      type: object
-      properties:
-        name:
-          type: string
-    Dog:
-      type: object
-      properties:
-        bark:
-          type: string
-    Lizard:
-      type: object
-      properties:
-        lovesRocks:
-          type: boolean
-
-    MyResponseType:
-      oneOf:
-      - $ref: '#/components/schemas/Cat'
-      - $ref: '#/components/schemas/Dog'
-      - $ref: '#/components/schemas/Lizard'
-      discriminator:
-        propertyName: pet_type
-        mapping:
-          dog: '#/components/schemas/Dog'
\ No newline at end of file
diff --git a/core/src/test/resources/issue-317_1.json b/core/src/test/resources/issue-317_1.json
new file mode 100644
index 000000000..ab6f7357b
--- /dev/null
+++ b/core/src/test/resources/issue-317_1.json
@@ -0,0 +1,84 @@
+{
+  "openapi":"3.0.0",
+  "info":{
+    "title":"API",
+    "version":"0.1.0"
+  },
+  "paths":{
+    "/resource":{
+      "post":{
+        "responses":{
+          "200":{
+            "description":"Created resource",
+            "content":{
+              "application/json":{
+                "schema":{
+                  "type":"string"
+                }
+              }
+            }
+          }
+        },
+        "summary":"Create resource",
+        "requestBody":{
+          "content":{
+            "application/json":{
+              "schema":{
+                "$ref":"#/components/schemas/Resource"
+              }
+            }
+          },
+          "description":"Definition of the resource"
+        }
+      }
+    }
+  },
+  "components":{
+    "schemas":{
+      "Resource":{
+        "type":"object",
+        "properties":{
+          "assignment":{
+            "$ref":"#/components/schemas/Foo"
+          }
+        }
+      },
+      "Foo":{
+        "oneOf":[
+          {
+            "$ref":"#/components/schemas/Foo.Bar"
+          },
+          {
+            "$ref":"#/components/schemas/Foo.Baz"
+          }
+        ],
+        "discriminator":{
+          "propertyName":"type",
+          "mapping":{
+            "Bar":"#/components/schemas/Foo.Bar",
+            "Baz":"#/components/schemas/Foo.Baz"
+          }
+        }
+      },
+      "Foo.Bar":{
+        "type":"object",
+        "properties":{
+          "type":{
+            "type":"string"
+          }
+        }
+      },
+      "Foo.Baz":{
+        "type":"object",
+        "properties":{
+          "type":{
+            "type":"string"
+          }
+        }
+      }
+    },
+    "securitySchemes":{
+
+    }
+  }
+}
diff --git a/core/src/test/resources/issue-317_2.json b/core/src/test/resources/issue-317_2.json
new file mode 100644
index 000000000..b3d35cde4
--- /dev/null
+++ b/core/src/test/resources/issue-317_2.json
@@ -0,0 +1,91 @@
+{
+  "openapi":"3.0.0",
+  "info":{
+    "title":"API",
+    "version":"0.1.0"
+  },
+  "paths":{
+    "/resource":{
+      "post":{
+        "responses":{
+          "200":{
+            "description":"Created resource",
+            "content":{
+              "application/json":{
+                "schema":{
+                  "type":"string"
+                }
+              }
+            }
+          }
+        },
+        "summary":"Create resource",
+        "requestBody":{
+          "content":{
+            "application/json":{
+              "schema":{
+                "$ref":"#/components/schemas/Resource"
+              }
+            }
+          },
+          "description":"Definition of the resource"
+        }
+      }
+    }
+  },
+  "components":{
+    "schemas":{
+      "Resource":{
+        "type":"object",
+        "properties":{
+          "assignment":{
+            "default":{
+              "type":"Bar"
+            },
+            "allOf":[
+              {
+                "$ref":"#/components/schemas/Foo"
+              }
+            ]
+          }
+        }
+      },
+      "Foo":{
+        "oneOf":[
+          {
+            "$ref":"#/components/schemas/Foo.Bar"
+          },
+          {
+            "$ref":"#/components/schemas/Foo.Baz"
+          }
+        ],
+        "discriminator":{
+          "propertyName":"type",
+          "mapping":{
+            "Bar":"#/components/schemas/Foo.Bar",
+            "Baz":"#/components/schemas/Foo.Baz"
+          }
+        }
+      },
+      "Foo.Bar":{
+        "type":"object",
+        "properties":{
+          "type":{
+            "type":"string"
+          }
+        }
+      },
+      "Foo.Baz":{
+        "type":"object",
+        "properties":{
+          "type":{
+            "type":"string"
+          }
+        }
+      }
+    },
+    "securitySchemes":{
+
+    }
+  }
+}
diff --git a/core/src/test/resources/issue-666-oas-3-0-examples-handling-1.yaml b/core/src/test/resources/issue-666-oas-3-0-examples-handling-1.yaml
new file mode 100644
index 000000000..041db37f6
--- /dev/null
+++ b/core/src/test/resources/issue-666-oas-3-0-examples-handling-1.yaml
@@ -0,0 +1,126 @@
+openapi: 3.0.2
+info:
+  description: Examples handling
+  title: Examples
+  version: 1.0.0
+paths:
+  /example/request/media-type:
+    post:
+      requestBody:
+        content:
+          application/json:
+            example: simple example
+            schema:
+              type: string
+      responses:
+        '200':
+          description: description
+          content:
+            application/json:
+              schema:
+                type: string
+  /example/response/media-type:
+    post:
+      requestBody:
+        content:
+          application/json:
+            schema:
+              type: string
+      responses:
+        '200':
+          description: description
+          content:
+            application/json:
+              example: simple example
+              schema:
+                type: string
+  /example/parameter/{id}:
+    post:
+      parameters:
+        - name: id
+          in: path
+          example: example
+          schema:
+            type: string
+      requestBody:
+        content:
+          application/json:
+            schema:
+              type: string
+      responses:
+        '200':
+          description: description
+          content:
+            application/json:
+              schema:
+                type: string
+  /example/response/header:
+    post:
+      requestBody:
+        content:
+          application/json:
+            schema:
+              type: string
+      responses:
+        '200':
+          description: description
+          headers:
+            X-Header:
+              example: example
+              schema:
+                type: integer
+                format: int32
+          content:
+            application/json:
+              schema:
+                type: string
+  /example/model/property:
+    post:
+      requestBody:
+        content:
+          application/json:
+            schema:
+              $ref: "#/components/schemas/TestDTO"
+      responses:
+        '200':
+          description: description
+          content:
+            application/json:
+              schema:
+                type: string
+  /example/model/property/composed:
+    post:
+      requestBody:
+        content:
+          application/json:
+            schema:
+              $ref: "#/components/schemas/TestComposedDTO"
+      responses:
+        '200':
+          description: description
+          content:
+            application/json:
+              schema:
+                type: string
+components:
+  schemas:
+    TestDTO:
+      type: object
+      properties:
+        field1:
+          example: example
+          type: string
+    TestDTO2:
+      type: object
+      properties:
+        field2:
+          example: example
+          type: string
+    TestComposedDTO:
+      type: object
+      properties:
+        field1:
+          type: object
+          allOf:
+            - $ref: "#/components/schemas/TestDTO"
+            - $ref: "#/components/schemas/TestDTO2"
\ No newline at end of file
diff --git a/core/src/test/resources/issue-666-oas-3-0-examples-handling-2.yaml b/core/src/test/resources/issue-666-oas-3-0-examples-handling-2.yaml
new file mode 100644
index 000000000..e0d5914a5
--- /dev/null
+++ b/core/src/test/resources/issue-666-oas-3-0-examples-handling-2.yaml
@@ -0,0 +1,126 @@
+openapi: 3.0.2
+info:
+  description: Examples handling
+  title: Examples
+  version: 2.0.0
+paths:
+  /example/request/media-type:
+    post:
+      requestBody:
+        content:
+          application/json:
+            example: simple example updated
+            schema:
+              type: string
+      responses:
+        '200':
+          description: description
+          content:
+            application/json:
+              schema:
+                type: string
+  /example/response/media-type:
+    post:
+      requestBody:
+        content:
+          application/json:
+            schema:
+              type: string
+      responses:
+        '200':
+          description: description
+          content:
+            application/json:
+              example: simple example updated
+              schema:
+                type: string
+  /example/parameter/{id}:
+    post:
+      parameters:
+        - name: id
+          in: path
+          example: example updated
+          schema:
+            type: string
+      requestBody:
+        content:
+          application/json:
+            schema:
+              type: string
+      responses:
+        '200':
+          description: description
+          content:
+            application/json:
+              schema:
+                type: string
+  /example/response/header:
+    post:
+      requestBody:
+        content:
+          application/json:
+            schema:
+              type: string
+      responses:
+        '200':
+          description: description
+          headers:
+            X-Header:
+              example: updated example
+              schema:
+                type: integer
+                format: int32
+          content:
+            application/json:
+              schema:
+                type: string
+  /example/model/property:
+    post:
+      requestBody:
+        content:
+          application/json:
+            schema:
+              $ref: "#/components/schemas/TestDTO"
+      responses:
+        '200':
+          description: description
+          content:
+            application/json:
+              schema:
+                type: string
+  /example/model/property/composed:
+    post:
+      requestBody:
+        content:
+          application/json:
+            schema:
+              $ref: "#/components/schemas/TestComposedDTO"
+      responses:
+        '200':
+          description: description
+          content:
+            application/json:
+              schema:
+                type: string
+components:
+  schemas:
+    TestDTO:
+      type: object
+      properties:
+        field1:
+          example: example updated
+          type: string
+    TestDTO2:
+      type: object
+      properties:
+        field2:
+          example: example updated
+          type: string
+    TestComposedDTO:
+      type: object
+      properties:
+        field1:
+          type: object
+          allOf:
+            - $ref: "#/components/schemas/TestDTO"
+            - $ref: "#/components/schemas/TestDTO2"
\ No newline at end of file
diff --git a/core/src/test/resources/issue-666-oas-3-1-examples-handling-1.yaml b/core/src/test/resources/issue-666-oas-3-1-examples-handling-1.yaml
new file mode 100644
index 000000000..e03ea0090
--- /dev/null
+++ b/core/src/test/resources/issue-666-oas-3-1-examples-handling-1.yaml
@@ -0,0 +1,152 @@
+openapi: 3.1.0
+info:
+  description: Examples handling
+  title: Examples
+  version: 1.0.0
+paths:
+  /example/request/media-type:
+    post:
+      requestBody:
+        content:
+          application/json:
+            example: simple example
+            schema:
+              type: string
+      responses:
+        '200':
+          content:
+            application/json:
+              schema:
+                type: string
+  /examples/request/media-type:
+    post:
+      requestBody:
+        content:
+          application/json:
+            examples:
+              first: example
+              second: example
+            schema:
+              type: string
+      responses:
+        '200':
+          content:
+            application/json:
+              schema:
+                type: string
+  /example/response/media-type:
+    post:
+      requestBody:
+        content:
+          application/json:
+            schema:
+              type: string
+      responses:
+        '200':
+          content:
+            application/json:
+              example: simple example
+              schema:
+                type: string
+  /examples/response/media-type:
+    post:
+      requestBody:
+        content:
+          application/json:
+            schema:
+              type: string
+      responses:
+        '200':
+          content:
+            application/json:
+              examples:
+                first: example
+                second: example
+              schema:
+                type: string
+  /example/parameter/{id}:
+    post:
+      parameters:
+        - name: id
+          in: path
+          example: example
+          schema:
+            type: string
+      requestBody:
+        content:
+          application/json:
+            schema:
+              type: string
+      responses:
+        '200':
+          content:
+            application/json:
+              schema:
+                type: string
+  /examples/parameter/{id}:
+    post:
+      parameters:
+        - name: id
+          in: path
+          examples:
+            first: example
+            second: example
+          schema:
+            type: string
+      requestBody:
+        content:
+          application/json:
+            schema:
+              type: string
+      responses:
+        '200':
+          content:
+            application/json:
+              schema:
+                type: string
+  /examples/response/header:
+    post:
+      requestBody:
+        content:
+          application/json:
+            schema:
+              type: string
+      responses:
+        '200':
+          description: description
+          headers:
+            X-Header:
+              examples:
+                first: example
+                second: example
+              schema:
+                type: integer
+                format: int32
+          content:
+            application/json:
+              schema:
+                type: string
+  /examples/model/property:
+    post:
+      requestBody:
+        content:
+          application/json:
+            schema:
+              $ref: "#/components/schemas/TestDTO"
+      responses:
+        '200':
+          description: description
+          content:
+            application/json:
+              schema:
+                type: string
+components:
+  schemas:
+    TestDTO:
+      type: object
+      properties:
+        field1:
+          examples:
+            - first example
+            - second example
+          type: string
\ No newline at end of file
diff --git a/core/src/test/resources/issue-666-oas-3-1-examples-handling-2.yaml b/core/src/test/resources/issue-666-oas-3-1-examples-handling-2.yaml
new file mode 100644
index 000000000..26d11d91d
--- /dev/null
+++ b/core/src/test/resources/issue-666-oas-3-1-examples-handling-2.yaml
@@ -0,0 +1,152 @@
+openapi: 3.1.0
+info:
+  description: Examples handling
+  title: Examples
+  version: 2.0.0
+paths:
+  /example/request/media-type:
+    post:
+      requestBody:
+        content:
+          application/json:
+            example: simple example updated
+            schema:
+              type: string
+      responses:
+        '200':
+          content:
+            application/json:
+              schema:
+                type: string
+  /examples/request/media-type:
+    post:
+      requestBody:
+        content:
+          application/json:
+            examples:
+              first: example updated
+              second: example updated
+            schema:
+              type: string
+      responses:
+        '200':
+          content:
+            application/json:
+              schema:
+                type: string
+  /example/response/media-type:
+    post:
+      requestBody:
+        content:
+          application/json:
+            schema:
+              type: string
+      responses:
+        '200':
+          content:
+            application/json:
+              example: simple example updated
+              schema:
+                type: string
+  /examples/response/media-type:
+    post:
+      requestBody:
+        content:
+          application/json:
+            schema:
+              type: string
+      responses:
+        '200':
+          content:
+            application/json:
+              examples:
+                first: example updated
+                second: example updated
+              schema:
+                type: string
+  /example/parameter/{id}:
+    post:
+      parameters:
+        - name: id
+          in: path
+          example: example updated
+          schema:
+            type: string
+      requestBody:
+        content:
+          application/json:
+            schema:
+              type: string
+      responses:
+        '200':
+          content:
+            application/json:
+              schema:
+                type: string
+  /examples/parameter/{id}:
+    post:
+      parameters:
+        - name: id
+          in: path
+          examples:
+            first: example updated
+            second: example updated
+          schema:
+            type: string
+      requestBody:
+        content:
+          application/json:
+            schema:
+              type: string
+      responses:
+        '200':
+          content:
+            application/json:
+              schema:
+                type: string
+  /examples/response/header:
+    post:
+      requestBody:
+        content:
+          application/json:
+            schema:
+              type: string
+      responses:
+        '200':
+          description: description
+          headers:
+            X-Header:
+              examples:
+                first: example updated
+                second: example updated
+              schema:
+                type: integer
+                format: int32
+          content:
+            application/json:
+              schema:
+                type: string
+  /examples/model/property:
+    post:
+      requestBody:
+        content:
+          application/json:
+            schema:
+              $ref: "#/components/schemas/TestDTO"
+      responses:
+        '200':
+          description: description
+          content:
+            application/json:
+              schema:
+                type: string
+components:
+  schemas:
+    TestDTO:
+      type: object
+      properties:
+        field1:
+          examples:
+            - first example updated
+            - second example updated
+          type: string
\ No newline at end of file
diff --git a/core/src/test/resources/jsr310_property_1.yaml b/core/src/test/resources/jsr310_property_1.yaml
new file mode 100644
index 000000000..7257579ce
--- /dev/null
+++ b/core/src/test/resources/jsr310_property_1.yaml
@@ -0,0 +1,23 @@
+openapi: 3.0.1
+info:
+  title: Title
+  version: 1.0.0
+  description: Description
+paths:
+  /:
+    get:
+      summary: Simple GET
+      operationId: simpleGet
+      responses:
+        default:
+          description: Default response
+          content:
+            application/json:
+              schema:
+                properties:
+                  someDateTime:
+                    description: Date time
+                    example: 2021-11-03T13:50:47Z
+                    type: string
+                    format: date-time
+      description: Simple GET
\ No newline at end of file
diff --git a/core/src/test/resources/jsr310_property_2.yaml b/core/src/test/resources/jsr310_property_2.yaml
new file mode 100644
index 000000000..a51d7abc4
--- /dev/null
+++ b/core/src/test/resources/jsr310_property_2.yaml
@@ -0,0 +1,22 @@
+openapi: 3.0.1
+info:
+  title: Title
+  version: 1.0.0
+  description: Description
+paths:
+  /:
+    get:
+      summary: Simple GET
+      operationId: simpleGet
+      responses:
+        default:
+          description: Default response
+          content:
+            application/json:
+              schema:
+                properties:
+                  someDateTime:
+                    description: Date time
+                    example: 2021-11-03T13:50:47Z
+                    type: string
+      description: Simple GET
\ No newline at end of file
diff --git a/core/src/test/resources/logback-test.xml b/core/src/test/resources/logback-test.xml
new file mode 100644
index 000000000..018a4277c
--- /dev/null
+++ b/core/src/test/resources/logback-test.xml
@@ -0,0 +1,11 @@
+<configuration>
+  <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
+    <encoder>
+      <pattern>[%thread] %-5level %logger{36} - %msg%n</pattern>
+    </encoder>
+  </appender>
+
+  <root level="debug">
+    <appender-ref ref="STDOUT"/>
+  </root>
+</configuration>
\ No newline at end of file
diff --git a/core/src/test/resources/operationDiff/operation_diff_1.yaml b/core/src/test/resources/operationDiff/operation_diff_1.yaml
new file mode 100644
index 000000000..e0faf2df2
--- /dev/null
+++ b/core/src/test/resources/operationDiff/operation_diff_1.yaml
@@ -0,0 +1,54 @@
+---
+openapi: "3.0.1"
+info:
+  title: "Test title"
+  description: "This is a test metadata"
+  termsOfService: "http://test.com"
+  contact:
+    name: "Mark Snijder"
+    url: "marksnijder.nl"
+    email: "snijderd@gmail.com"
+  license:
+    name: "To be decided"
+    url: "http://test.com"
+  version: "version 1.0"
+paths:
+  /operation/operation-id:
+    get:
+      operationId: operation
+      responses:
+        '200':
+          description: response
+          content:
+            application/json: {}
+  /operation/summary:
+    get:
+      summary: summary
+      responses:
+        '200':
+          description: response
+          content:
+            application/json: { }
+  /operation/description:
+    get:
+      description: description
+      responses:
+        '200':
+          description: response
+          content:
+            application/json: { }
+  /operation/becomes-deprecated:
+    get:
+      responses:
+        '200':
+          description: response
+          content:
+            application/json: { }
+  /operation/becomes-not-deprecated:
+    get:
+      deprecated: true
+      responses:
+        '200':
+          description: response
+          content:
+            application/json: { }
diff --git a/core/src/test/resources/operationDiff/operation_diff_2.yaml b/core/src/test/resources/operationDiff/operation_diff_2.yaml
new file mode 100644
index 000000000..8f5124955
--- /dev/null
+++ b/core/src/test/resources/operationDiff/operation_diff_2.yaml
@@ -0,0 +1,55 @@
+---
+openapi: "3.0.1"
+info:
+  title: "Test title"
+  description: "This is a test metadata"
+  termsOfService: "http://test.com"
+  contact:
+    name: "Mark Snijder"
+    url: "marksnijder.nl"
+    email: "snijderd@gmail.com"
+  license:
+    name: "To be decided"
+    url: "http://test.com"
+  version: "version 1.0"
+paths:
+  /operation/operation-id:
+    get:
+      operationId: changed
+      responses:
+        '200':
+          description: response
+          content:
+            application/json: {}
+  /operation/summary:
+    get:
+      summary: changed
+      responses:
+        '200':
+          description: response
+          content:
+            application/json: { }
+  /operation/description:
+    get:
+      description: changed
+      responses:
+        '200':
+          description: response
+          content:
+            application/json: { }
+  /operation/becomes-deprecated:
+    get:
+      deprecated: true
+      responses:
+        '200':
+          description: response
+          content:
+            application/json: { }
+  /operation/becomes-not-deprecated:
+    get:
+      deprecated: false
+      responses:
+        '200':
+          description: response
+          content:
+            application/json: { }
diff --git a/core/src/test/resources/operation_diff_1.yaml b/core/src/test/resources/operation_diff_1.yaml
deleted file mode 100644
index 0618b58e6..000000000
--- a/core/src/test/resources/operation_diff_1.yaml
+++ /dev/null
@@ -1,24 +0,0 @@
----
-openapi: "3.0.1"
-info:
-  title: "Test title"
-  description: "This is a test metadata"
-  termsOfService: "http://test.com"
-  contact:
-    name: "Mark Snijder"
-    url: "marksnijder.nl"
-    email: "snijderd@gmail.com"
-  license:
-    name: "To be decided"
-    url: "http://test.com"
-  version: "version 1.0"
-paths:
-  /pets/{id}:
-    get:
-      description: Returns a user based on a single ID, if the user does not have access to the pet
-      operationId: operation
-      responses:
-        '200':
-          description: response
-          content:
-            application/json: {}
diff --git a/core/src/test/resources/operation_diff_2.yaml b/core/src/test/resources/operation_diff_2.yaml
deleted file mode 100644
index 8d60a2dc1..000000000
--- a/core/src/test/resources/operation_diff_2.yaml
+++ /dev/null
@@ -1,24 +0,0 @@
----
-openapi: "3.0.1"
-info:
-  title: "Test title"
-  description: "This is a test metadata"
-  termsOfService: "http://test.com"
-  contact:
-    name: "Mark Snijder"
-    url: "marksnijder.nl"
-    email: "snijderd@gmail.com"
-  license:
-    name: "To be decided"
-    url: "http://test.com"
-  version: "version 1.0"
-paths:
-  /pets/{id}:
-    get:
-      description: Returns a user based on a single ID, if the user does not have access to the pet
-      operationId: changed
-      responses:
-        '200':
-          description: response
-          content:
-            application/json: {}
diff --git a/core/src/test/resources/parameterDiff/issue-458-integer-limits_1.yaml b/core/src/test/resources/parameterDiff/issue-458-integer-limits_1.yaml
new file mode 100644
index 000000000..16b769d5b
--- /dev/null
+++ b/core/src/test/resources/parameterDiff/issue-458-integer-limits_1.yaml
@@ -0,0 +1,34 @@
+openapi: 3.0.0
+servers:
+  - url: 'http://petstore.swagger.io/v2'
+info:
+  description: >-
+    Description
+  version: 1.0.0
+  title: Swagger Petstore
+  termsOfService: 'http://swagger.io/terms/'
+  contact:
+    email: apiteam@swagger.io
+  license:
+    name: Apache 2.0
+    url: 'http://www.apache.org/licenses/LICENSE-2.0.html'
+paths:
+  /int-limits:
+    get:
+      summary: get int
+      description: 'description'
+      operationId: getInt
+      parameters:
+        - name: int
+          in: query
+          required: true
+          schema:
+            type: integer
+            format: int32
+            minimum: 10
+            maximum: 100
+            exclusiveMinimum: false
+            exclusiveMaximum: false
+      responses:
+        '200':
+          description: OK
diff --git a/core/src/test/resources/parameterDiff/issue-458-integer-limits_10.yaml b/core/src/test/resources/parameterDiff/issue-458-integer-limits_10.yaml
new file mode 100644
index 000000000..e704c4f9c
--- /dev/null
+++ b/core/src/test/resources/parameterDiff/issue-458-integer-limits_10.yaml
@@ -0,0 +1,33 @@
+openapi: 3.0.0
+servers:
+  - url: 'http://petstore.swagger.io/v2'
+info:
+  description: >-
+    Description
+  version: 1.0.0
+  title: Swagger Petstore
+  termsOfService: 'http://swagger.io/terms/'
+  contact:
+    email: apiteam@swagger.io
+  license:
+    name: Apache 2.0
+    url: 'http://www.apache.org/licenses/LICENSE-2.0.html'
+paths:
+  /int-limits:
+    get:
+      summary: get int
+      description: 'description'
+      operationId: getInt
+      parameters:
+        - name: int
+          in: query
+          required: true
+          schema:
+            type: integer
+            format: int32
+            minimum: 10
+            maximum: 100
+            exclusiveMaximum: false
+      responses:
+        '200':
+          description: OK
diff --git a/core/src/test/resources/parameterDiff/issue-458-integer-limits_11.yaml b/core/src/test/resources/parameterDiff/issue-458-integer-limits_11.yaml
new file mode 100644
index 000000000..ea16b71a9
--- /dev/null
+++ b/core/src/test/resources/parameterDiff/issue-458-integer-limits_11.yaml
@@ -0,0 +1,34 @@
+openapi: 3.0.0
+servers:
+  - url: 'http://petstore.swagger.io/v2'
+info:
+  description: >-
+    Description
+  version: 1.0.0
+  title: Swagger Petstore
+  termsOfService: 'http://swagger.io/terms/'
+  contact:
+    email: apiteam@swagger.io
+  license:
+    name: Apache 2.0
+    url: 'http://www.apache.org/licenses/LICENSE-2.0.html'
+paths:
+  /int-limits:
+    get:
+      summary: get int
+      description: 'description'
+      operationId: getInt
+      parameters:
+        - name: int
+          in: query
+          required: true
+          schema:
+            type: integer
+            format: int32
+            minimum: 10
+            maximum: 100
+            exclusiveMinimum: true
+            exclusiveMaximum: true
+      responses:
+        '200':
+          description: OK
diff --git a/core/src/test/resources/parameterDiff/issue-458-integer-limits_12.yaml b/core/src/test/resources/parameterDiff/issue-458-integer-limits_12.yaml
new file mode 100644
index 000000000..1c410a34d
--- /dev/null
+++ b/core/src/test/resources/parameterDiff/issue-458-integer-limits_12.yaml
@@ -0,0 +1,34 @@
+openapi: 3.0.0
+servers:
+  - url: 'http://petstore.swagger.io/v2'
+info:
+  description: >-
+    Description
+  version: 1.0.0
+  title: Swagger Petstore
+  termsOfService: 'http://swagger.io/terms/'
+  contact:
+    email: apiteam@swagger.io
+  license:
+    name: Apache 2.0
+    url: 'http://www.apache.org/licenses/LICENSE-2.0.html'
+paths:
+  /int-limits:
+    get:
+      summary: get int
+      description: 'description'
+      operationId: getInt
+      parameters:
+        - name: int
+          in: query
+          required: true
+          schema:
+            type: integer
+            format: int32
+            minimum: 10
+            maximum: 100
+            exclusiveMinimum: true
+            exclusiveMaximum: false
+      responses:
+        '200':
+          description: OK
diff --git a/core/src/test/resources/parameterDiff/issue-458-integer-limits_13.yaml b/core/src/test/resources/parameterDiff/issue-458-integer-limits_13.yaml
new file mode 100644
index 000000000..896a9c32e
--- /dev/null
+++ b/core/src/test/resources/parameterDiff/issue-458-integer-limits_13.yaml
@@ -0,0 +1,34 @@
+openapi: 3.0.0
+servers:
+  - url: 'http://petstore.swagger.io/v2'
+info:
+  description: >-
+    Description
+  version: 1.0.0
+  title: Swagger Petstore
+  termsOfService: 'http://swagger.io/terms/'
+  contact:
+    email: apiteam@swagger.io
+  license:
+    name: Apache 2.0
+    url: 'http://www.apache.org/licenses/LICENSE-2.0.html'
+paths:
+  /int-limits:
+    get:
+      summary: get int
+      description: 'description'
+      operationId: getInt
+      parameters:
+        - name: int
+          in: query
+          required: true
+          schema:
+            type: integer
+            format: int32
+            minimum: 10
+            maximum: 100
+            exclusiveMinimum: false
+            exclusiveMaximum: true
+      responses:
+        '200':
+          description: OK
diff --git a/core/src/test/resources/parameterDiff/issue-458-integer-limits_14.yaml b/core/src/test/resources/parameterDiff/issue-458-integer-limits_14.yaml
new file mode 100644
index 000000000..7fa4e1f50
--- /dev/null
+++ b/core/src/test/resources/parameterDiff/issue-458-integer-limits_14.yaml
@@ -0,0 +1,33 @@
+openapi: 3.0.0
+servers:
+  - url: 'http://petstore.swagger.io/v2'
+info:
+  description: >-
+    Description
+  version: 1.0.0
+  title: Swagger Petstore
+  termsOfService: 'http://swagger.io/terms/'
+  contact:
+    email: apiteam@swagger.io
+  license:
+    name: Apache 2.0
+    url: 'http://www.apache.org/licenses/LICENSE-2.0.html'
+paths:
+  /int-limits:
+    get:
+      summary: get int
+      description: 'description'
+      operationId: getInt
+      parameters:
+        - name: int
+          in: query
+          required: true
+          schema:
+            type: integer
+            format: int32
+            minimum: 10
+            maximum: 100
+            exclusiveMinimum: true
+      responses:
+        '200':
+          description: OK
diff --git a/core/src/test/resources/parameterDiff/issue-458-integer-limits_15.yaml b/core/src/test/resources/parameterDiff/issue-458-integer-limits_15.yaml
new file mode 100644
index 000000000..9bd90be6f
--- /dev/null
+++ b/core/src/test/resources/parameterDiff/issue-458-integer-limits_15.yaml
@@ -0,0 +1,33 @@
+openapi: 3.0.0
+servers:
+  - url: 'http://petstore.swagger.io/v2'
+info:
+  description: >-
+    Description
+  version: 1.0.0
+  title: Swagger Petstore
+  termsOfService: 'http://swagger.io/terms/'
+  contact:
+    email: apiteam@swagger.io
+  license:
+    name: Apache 2.0
+    url: 'http://www.apache.org/licenses/LICENSE-2.0.html'
+paths:
+  /int-limits:
+    get:
+      summary: get int
+      description: 'description'
+      operationId: getInt
+      parameters:
+        - name: int
+          in: query
+          required: true
+          schema:
+            type: integer
+            format: int32
+            minimum: 10
+            maximum: 100
+            exclusiveMaximum: true
+      responses:
+        '200':
+          description: OK
diff --git a/core/src/test/resources/parameterDiff/issue-458-integer-limits_2.yaml b/core/src/test/resources/parameterDiff/issue-458-integer-limits_2.yaml
new file mode 100644
index 000000000..55c1c1983
--- /dev/null
+++ b/core/src/test/resources/parameterDiff/issue-458-integer-limits_2.yaml
@@ -0,0 +1,34 @@
+openapi: 3.0.0
+servers:
+  - url: 'http://petstore.swagger.io/v2'
+info:
+  description: >-
+    Description
+  version: 1.0.0
+  title: Swagger Petstore
+  termsOfService: 'http://swagger.io/terms/'
+  contact:
+    email: apiteam@swagger.io
+  license:
+    name: Apache 2.0
+    url: 'http://www.apache.org/licenses/LICENSE-2.0.html'
+paths:
+  /int-limits:
+    get:
+      summary: get int
+      description: 'description'
+      operationId: getInt
+      parameters:
+        - name: int
+          in: query
+          required: true
+          schema:
+            type: integer
+            format: int32
+            minimum: 10
+            maximum: 99
+            exclusiveMinimum: false
+            exclusiveMaximum: false
+      responses:
+        '200':
+          description: OK
diff --git a/core/src/test/resources/parameterDiff/issue-458-integer-limits_3.yaml b/core/src/test/resources/parameterDiff/issue-458-integer-limits_3.yaml
new file mode 100644
index 000000000..f30ef6501
--- /dev/null
+++ b/core/src/test/resources/parameterDiff/issue-458-integer-limits_3.yaml
@@ -0,0 +1,34 @@
+openapi: 3.0.0
+servers:
+  - url: 'http://petstore.swagger.io/v2'
+info:
+  description: >-
+    Description
+  version: 1.0.0
+  title: Swagger Petstore
+  termsOfService: 'http://swagger.io/terms/'
+  contact:
+    email: apiteam@swagger.io
+  license:
+    name: Apache 2.0
+    url: 'http://www.apache.org/licenses/LICENSE-2.0.html'
+paths:
+  /int-limits:
+    get:
+      summary: get int
+      description: 'description'
+      operationId: getInt
+      parameters:
+        - name: int
+          in: query
+          required: true
+          schema:
+            type: integer
+            format: int32
+            minimum: 10
+            maximum: 101
+            exclusiveMinimum: false
+            exclusiveMaximum: false
+      responses:
+        '200':
+          description: OK
diff --git a/core/src/test/resources/parameterDiff/issue-458-integer-limits_4.yaml b/core/src/test/resources/parameterDiff/issue-458-integer-limits_4.yaml
new file mode 100644
index 000000000..91cc048c2
--- /dev/null
+++ b/core/src/test/resources/parameterDiff/issue-458-integer-limits_4.yaml
@@ -0,0 +1,34 @@
+openapi: 3.0.0
+servers:
+  - url: 'http://petstore.swagger.io/v2'
+info:
+  description: >-
+    Description
+  version: 1.0.0
+  title: Swagger Petstore
+  termsOfService: 'http://swagger.io/terms/'
+  contact:
+    email: apiteam@swagger.io
+  license:
+    name: Apache 2.0
+    url: 'http://www.apache.org/licenses/LICENSE-2.0.html'
+paths:
+  /int-limits:
+    get:
+      summary: get int
+      description: 'description'
+      operationId: getInt
+      parameters:
+        - name: int
+          in: query
+          required: true
+          schema:
+            type: integer
+            format: int32
+            minimum: 9
+            maximum: 100
+            exclusiveMinimum: false
+            exclusiveMaximum: false
+      responses:
+        '200':
+          description: OK
diff --git a/core/src/test/resources/parameterDiff/issue-458-integer-limits_5.yaml b/core/src/test/resources/parameterDiff/issue-458-integer-limits_5.yaml
new file mode 100644
index 000000000..4881d7fd9
--- /dev/null
+++ b/core/src/test/resources/parameterDiff/issue-458-integer-limits_5.yaml
@@ -0,0 +1,34 @@
+openapi: 3.0.0
+servers:
+  - url: 'http://petstore.swagger.io/v2'
+info:
+  description: >-
+    Description
+  version: 1.0.0
+  title: Swagger Petstore
+  termsOfService: 'http://swagger.io/terms/'
+  contact:
+    email: apiteam@swagger.io
+  license:
+    name: Apache 2.0
+    url: 'http://www.apache.org/licenses/LICENSE-2.0.html'
+paths:
+  /int-limits:
+    get:
+      summary: get int
+      description: 'description'
+      operationId: getInt
+      parameters:
+        - name: int
+          in: query
+          required: true
+          schema:
+            type: integer
+            format: int32
+            minimum: 11
+            maximum: 100
+            exclusiveMinimum: false
+            exclusiveMaximum: false
+      responses:
+        '200':
+          description: OK
diff --git a/core/src/test/resources/parameterDiff/issue-458-integer-limits_6.yaml b/core/src/test/resources/parameterDiff/issue-458-integer-limits_6.yaml
new file mode 100644
index 000000000..dbcaa9bed
--- /dev/null
+++ b/core/src/test/resources/parameterDiff/issue-458-integer-limits_6.yaml
@@ -0,0 +1,34 @@
+openapi: 3.0.0
+servers:
+  - url: 'http://petstore.swagger.io/v2'
+info:
+  description: >-
+    Description
+  version: 1.0.0
+  title: Swagger Petstore
+  termsOfService: 'http://swagger.io/terms/'
+  contact:
+    email: apiteam@swagger.io
+  license:
+    name: Apache 2.0
+    url: 'http://www.apache.org/licenses/LICENSE-2.0.html'
+paths:
+  /int-limits:
+    get:
+      summary: get int
+      description: 'description'
+      operationId: getInt
+      parameters:
+        - name: int
+          in: query
+          required: true
+          schema:
+            type: integer
+            format: int64
+            minimum: 10
+            maximum: 100
+            exclusiveMinimum: false
+            exclusiveMaximum: false
+      responses:
+        '200':
+          description: OK
diff --git a/core/src/test/resources/parameterDiff/issue-458-integer-limits_7.yaml b/core/src/test/resources/parameterDiff/issue-458-integer-limits_7.yaml
new file mode 100644
index 000000000..1c410a34d
--- /dev/null
+++ b/core/src/test/resources/parameterDiff/issue-458-integer-limits_7.yaml
@@ -0,0 +1,34 @@
+openapi: 3.0.0
+servers:
+  - url: 'http://petstore.swagger.io/v2'
+info:
+  description: >-
+    Description
+  version: 1.0.0
+  title: Swagger Petstore
+  termsOfService: 'http://swagger.io/terms/'
+  contact:
+    email: apiteam@swagger.io
+  license:
+    name: Apache 2.0
+    url: 'http://www.apache.org/licenses/LICENSE-2.0.html'
+paths:
+  /int-limits:
+    get:
+      summary: get int
+      description: 'description'
+      operationId: getInt
+      parameters:
+        - name: int
+          in: query
+          required: true
+          schema:
+            type: integer
+            format: int32
+            minimum: 10
+            maximum: 100
+            exclusiveMinimum: true
+            exclusiveMaximum: false
+      responses:
+        '200':
+          description: OK
diff --git a/core/src/test/resources/parameterDiff/issue-458-integer-limits_8.yaml b/core/src/test/resources/parameterDiff/issue-458-integer-limits_8.yaml
new file mode 100644
index 000000000..896a9c32e
--- /dev/null
+++ b/core/src/test/resources/parameterDiff/issue-458-integer-limits_8.yaml
@@ -0,0 +1,34 @@
+openapi: 3.0.0
+servers:
+  - url: 'http://petstore.swagger.io/v2'
+info:
+  description: >-
+    Description
+  version: 1.0.0
+  title: Swagger Petstore
+  termsOfService: 'http://swagger.io/terms/'
+  contact:
+    email: apiteam@swagger.io
+  license:
+    name: Apache 2.0
+    url: 'http://www.apache.org/licenses/LICENSE-2.0.html'
+paths:
+  /int-limits:
+    get:
+      summary: get int
+      description: 'description'
+      operationId: getInt
+      parameters:
+        - name: int
+          in: query
+          required: true
+          schema:
+            type: integer
+            format: int32
+            minimum: 10
+            maximum: 100
+            exclusiveMinimum: false
+            exclusiveMaximum: true
+      responses:
+        '200':
+          description: OK
diff --git a/core/src/test/resources/parameterDiff/issue-458-integer-limits_9.yaml b/core/src/test/resources/parameterDiff/issue-458-integer-limits_9.yaml
new file mode 100644
index 000000000..8dbf0cd3e
--- /dev/null
+++ b/core/src/test/resources/parameterDiff/issue-458-integer-limits_9.yaml
@@ -0,0 +1,33 @@
+openapi: 3.0.0
+servers:
+  - url: 'http://petstore.swagger.io/v2'
+info:
+  description: >-
+    Description
+  version: 1.0.0
+  title: Swagger Petstore
+  termsOfService: 'http://swagger.io/terms/'
+  contact:
+    email: apiteam@swagger.io
+  license:
+    name: Apache 2.0
+    url: 'http://www.apache.org/licenses/LICENSE-2.0.html'
+paths:
+  /int-limits:
+    get:
+      summary: get int
+      description: 'description'
+      operationId: getInt
+      parameters:
+        - name: int
+          in: query
+          required: true
+          schema:
+            type: integer
+            format: int32
+            minimum: 10
+            maximum: 100
+            exclusiveMinimum: false
+      responses:
+        '200':
+          description: OK
diff --git a/core/src/test/resources/parameterDiff/issue-488-1.json b/core/src/test/resources/parameterDiff/issue-488-1.json
new file mode 100644
index 000000000..22e0009dd
--- /dev/null
+++ b/core/src/test/resources/parameterDiff/issue-488-1.json
@@ -0,0 +1,98 @@
+{
+  "openapi": "3.0.1",
+  "info": {
+    "title": "Test-API",
+    "description": "Testdescription",
+    "version": "v1"
+  },
+  "paths": {
+    "/api/v1/test": {
+      "get": {
+        "tags": [
+          "TestTag"
+        ],
+        "summary": "Retrieve a list Test objects",
+        "description": "Usage with testId parameter is recommended.",
+        "parameters": [
+          {
+            "name": "testId",
+            "in": "query",
+            "description": "rename of prop before",
+            "schema": {
+              "type": "string"
+            }
+          },
+          {
+            "name": "dateFrom",
+            "in": "query",
+            "description": "Searches documents with creation date equal or younger specified timestamp",
+            "schema": {
+              "type": "string"
+            }
+          },
+          {
+            "name": "dateTo",
+            "in": "query",
+            "description": "Searches documents with creation date equal or older than specified timestamp",
+            "schema": {
+              "type": "string"
+            }
+          }
+        ],
+        "responses": {
+          "200": {
+            "description": "OK",
+            "content": {
+              "application/json": {
+                "schema": {
+                  "type": "array",
+                  "items": {
+                    "$ref": "#/components/schemas/TestDTO"
+                  }
+                }
+              }
+            }
+          },
+          "401": {
+            "description": "Unauthorized"
+          }
+        }
+      }
+    }
+  },
+  "components": {
+    "schemas": {
+      "TestDTO": {
+        "type": "object",
+        "properties": {
+          "Id": {
+            "type": "string",
+            "nullable": true
+          },
+          "Timestamp": {
+            "type": "string",
+            "format": "date-time"
+          },
+          "Test": {
+            "type": "string",
+            "nullable": true
+          }
+        },
+        "additionalProperties": false
+      }
+    },
+    "securitySchemes": {
+      "BearerAuth": {
+        "type": "apiKey",
+        "description": "Fill in your acquired bearer token here, must be like 'Bearer TOKEN_HERE'",
+        "name": "Authorization",
+        "in": "header"
+      }
+    }
+  },
+  "security": [
+    {
+      "BearerAuth": [ ]
+    }
+  ]
+}
\ No newline at end of file
diff --git a/core/src/test/resources/parameterDiff/issue-488-2.json b/core/src/test/resources/parameterDiff/issue-488-2.json
new file mode 100644
index 000000000..463e210c7
--- /dev/null
+++ b/core/src/test/resources/parameterDiff/issue-488-2.json
@@ -0,0 +1,98 @@
+{
+  "openapi": "3.0.1",
+  "info": {
+    "title": "Test-API",
+    "description": "Testdescription",
+    "version": "v1"
+  },
+  "paths": {
+    "/api/v1/test": {
+      "get": {
+        "tags": [
+          "TestTag"
+        ],
+        "summary": "Retrieve a list Test objects",
+        "description": "Usage with testId parameter is recommended.",
+        "parameters": [
+          {
+            "name": "testIdWillBreak",
+            "in": "query",
+            "description": "rename of prop after",
+            "schema": {
+              "type": "string"
+            }
+          },
+          {
+            "name": "dateFrom",
+            "in": "query",
+            "description": "Searches documents with creation date equal or younger specified timestamp",
+            "schema": {
+              "type": "string"
+            }
+          },
+          {
+            "name": "dateTo",
+            "in": "query",
+            "description": "Searches documents with creation date equal or older than specified timestamp",
+            "schema": {
+              "type": "string"
+            }
+          }
+        ],
+        "responses": {
+          "200": {
+            "description": "OK",
+            "content": {
+              "application/json": {
+                "schema": {
+                  "type": "array",
+                  "items": {
+                    "$ref": "#/components/schemas/TestDTO"
+                  }
+                }
+              }
+            }
+          },
+          "401": {
+            "description": "Unauthorized"
+          }
+        }
+      }
+    }
+  },
+  "components": {
+    "schemas": {
+      "TestDTO": {
+        "type": "object",
+        "properties": {
+          "Id": {
+            "type": "string",
+            "nullable": true
+          },
+          "Timestamp": {
+            "type": "string",
+            "format": "date-time"
+          },
+          "Test": {
+            "type": "string",
+            "nullable": true
+          }
+        },
+        "additionalProperties": false
+      }
+    },
+    "securitySchemes": {
+      "BearerAuth": {
+        "type": "apiKey",
+        "description": "Fill in your acquired bearer token here, must be like 'Bearer TOKEN_HERE'",
+        "name": "Authorization",
+        "in": "header"
+      }
+    }
+  },
+  "security": [
+    {
+      "BearerAuth": [ ]
+    }
+  ]
+}
\ No newline at end of file
diff --git a/core/src/test/resources/parameterDiff/parameter_diff_1.yaml b/core/src/test/resources/parameterDiff/parameter_diff_1.yaml
new file mode 100644
index 000000000..c17217338
--- /dev/null
+++ b/core/src/test/resources/parameterDiff/parameter_diff_1.yaml
@@ -0,0 +1,102 @@
+openapi: "3.1.0"
+info:
+  title: "Parameters diff test"
+  description: "TParameters diff test"
+  version: "1.0"
+paths:
+  /parameter/added:
+    get:
+      responses:
+        '200':
+          description: response
+          content:
+            application/json: { }
+  /parameter/removed:
+    get:
+      parameters:
+        - in: query
+          name: param
+          schema:
+            type: string
+      responses:
+        '200':
+          description: response
+          content:
+            application/json: { }
+  /parameter/becomes-deprecated:
+    get:
+      parameters:
+        - in: header
+          name: X-Header
+          schema:
+            type: string
+      responses:
+        '200':
+          description: response
+          content:
+            application/json: { }
+  /parameter/becomes-not-deprecated:
+    get:
+      parameters:
+        - in: header
+          name: X-Header
+          deprecated: true
+          schema:
+            type: string
+      responses:
+        '200':
+          description: response
+          content:
+            application/json: { }
+  /parameter/required:
+    get:
+      parameters:
+        - in: query
+          name: param
+          required: true
+          schema:
+            type: string
+      responses:
+        '200':
+          description: response
+          content:
+            application/json: { }
+  /parameter/description:
+    get:
+      parameters:
+        - in: query
+          name: param
+          description: description
+          schema:
+            type: string
+      responses:
+        '200':
+          description: response
+          content:
+            application/json: { }
+  /parameter/explode:
+    get:
+      parameters:
+        - in: query
+          name: param
+          schema:
+            type: string
+          explode: true
+      responses:
+        '200':
+          description: response
+          content:
+            application/json: { }
+  /parameter/style:
+    get:
+      parameters:
+        - in: query
+          name: param
+          schema:
+            type: string
+          style: simple
+      responses:
+        '200':
+          description: response
+          content:
+            application/json: { }
\ No newline at end of file
diff --git a/core/src/test/resources/parameterDiff/parameter_diff_2.yaml b/core/src/test/resources/parameterDiff/parameter_diff_2.yaml
new file mode 100644
index 000000000..0c30b6b1d
--- /dev/null
+++ b/core/src/test/resources/parameterDiff/parameter_diff_2.yaml
@@ -0,0 +1,102 @@
+openapi: "3.1.0"
+info:
+  title: "Parameters diff test"
+  description: "TParameters diff test"
+  version: "1.0"
+paths:
+  /parameter/added:
+    get:
+      parameters:
+        - in: query
+          name: param
+          schema:
+            type: string
+      responses:
+        '200':
+          description: response
+          content:
+            application/json: {}
+  /parameter/removed:
+    get:
+      responses:
+        '200':
+          description: response
+          content:
+            application/json: { }
+  /parameter/becomes-deprecated:
+    get:
+      parameters:
+        - in: header
+          name: X-Header
+          deprecated: true
+          schema:
+            type: string
+      responses:
+        '200':
+          description: response
+          content:
+            application/json: { }
+  /parameter/becomes-not-deprecated:
+    get:
+      parameters:
+        - in: header
+          name: X-Header
+          schema:
+            type: string
+      responses:
+        '200':
+          description: response
+          content:
+            application/json: { }
+  /parameter/required:
+    get:
+      parameters:
+        - in: query
+          name: param
+          required: false
+          schema:
+            type: string
+      responses:
+        '200':
+          description: response
+          content:
+            application/json: { }
+  /parameter/description:
+    get:
+      parameters:
+        - in: query
+          name: param
+          description: changed
+          schema:
+            type: string
+      responses:
+        '200':
+          description: response
+          content:
+            application/json: { }
+  /parameter/explode:
+    get:
+      parameters:
+        - in: query
+          name: param
+          schema:
+            type: string
+          explode: false
+      responses:
+        '200':
+          description: response
+          content:
+            application/json: { }
+  /parameter/style:
+    get:
+      parameters:
+        - in: query
+          name: param
+          schema:
+            type: string
+          style: matrix
+      responses:
+        '200':
+          description: response
+          content:
+            application/json: { }
\ No newline at end of file
diff --git a/core/src/test/resources/parameterDiff/parameters_overloading.yaml b/core/src/test/resources/parameterDiff/parameters_overloading.yaml
new file mode 100644
index 000000000..061444451
--- /dev/null
+++ b/core/src/test/resources/parameterDiff/parameters_overloading.yaml
@@ -0,0 +1,87 @@
+openapi: 3.0.2
+info:
+  title: Projects API
+  version: 1.0.0
+paths:
+  /projects/{key}:
+    get:
+      parameters:
+        - in: path
+          name: key
+          required: true
+          schema:
+            type: string
+      responses:
+        '200':
+          description: 'Success'
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/SampleResponse'
+  /projects/{id}:
+    get:
+      parameters:
+        - in: path
+          name: id
+          required: true
+          schema:
+            type: integer
+            format: int64
+      responses:
+        '200':
+          description: 'Success'
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/SampleResponse'
+  /projects/{uid}:
+    get:
+      parameters:
+        - in: path
+          name: uid
+          required: true
+          schema:
+            type: string
+            format: uuid
+      responses:
+        '200':
+          description: 'Success'
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/SampleResponse'
+  /projects/{id}.{idPostfix}:
+    get:
+      parameters:
+        - in: path
+          name: id
+          required: true
+          schema:
+            type: string
+        - in: path
+          name: idPostfix
+          required: true
+          schema:
+            type: string
+      responses:
+        '200':
+          description: 'Success'
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/SampleResponse'
+components:
+  schemas:
+    SampleResponse:
+      type: object
+      properties:
+        id:
+          type: integer
+        uid:
+          type: string
+        name:
+          type: string
+      required:
+        - id
+        - uid
+        - name
\ No newline at end of file
diff --git a/core/src/test/resources/parameterDiff/parameters_overloading_2.yaml b/core/src/test/resources/parameterDiff/parameters_overloading_2.yaml
new file mode 100644
index 000000000..d91b7a0f6
--- /dev/null
+++ b/core/src/test/resources/parameterDiff/parameters_overloading_2.yaml
@@ -0,0 +1,52 @@
+openapi: 3.0.2
+info:
+  title: Projects API
+  version: 1.0.0
+paths:
+  /projects/{id}:
+    get:
+      parameters:
+        - in: path
+          name: id
+          required: true
+          schema:
+            type: integer
+            format: int64
+      responses:
+        '200':
+          description: 'Success'
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/SampleResponse'
+  /projects/{uid}:
+    get:
+      parameters:
+        - in: path
+          name: uid
+          required: true
+          schema:
+            type: integer
+            format: int64
+      responses:
+        '200':
+          description: 'Success'
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/SampleResponse'
+components:
+  schemas:
+    SampleResponse:
+      type: object
+      properties:
+        id:
+          type: integer
+        uid:
+          type: string
+        name:
+          type: string
+      required:
+        - id
+        - uid
+        - name
\ No newline at end of file
diff --git a/core/src/test/resources/parameterDiff/path_parameter_diff_param_name_diff_new.yaml b/core/src/test/resources/parameterDiff/path_parameter_diff_param_name_diff_new.yaml
new file mode 100644
index 000000000..8faa0730e
--- /dev/null
+++ b/core/src/test/resources/parameterDiff/path_parameter_diff_param_name_diff_new.yaml
@@ -0,0 +1,48 @@
+openapi: 3.0.0
+info:
+  title: Sample API
+  description: Optional multiline or single-line description in [CommonMark](http://commonmark.org/help/) or HTML.
+  version: 0.1.9
+servers:
+  - url: http://api.example.com/v1
+    description: Example api server
+
+paths:
+
+  /student/{username}:
+    get:
+      parameters:
+        - in: path
+          name: username   # Note the name is the same as in the path
+          required: true
+          schema:
+            type: string
+            minimum: 1
+
+
+      summary: Returns data about a student
+      responses:
+        '200': # status code
+          description: A JSON Object student data
+          content:
+            application/json:
+              schema:
+                type: object
+                items:
+                  $ref: '#/components/schemas/Student'
+
+components:
+  schemas:
+    Student:
+      type: object
+      properties:
+        id:
+          type: integer
+          example: "1"
+        username:
+          type: string
+          example: "alloy_horizon"
+          pattern: A..Z a..z 0..9 . _ -
+        name:
+          type: string
+          example: "Alloy Horizon"
diff --git a/core/src/test/resources/parameterDiff/path_parameter_diff_param_name_diff_old.yaml b/core/src/test/resources/parameterDiff/path_parameter_diff_param_name_diff_old.yaml
new file mode 100644
index 000000000..7ced291d1
--- /dev/null
+++ b/core/src/test/resources/parameterDiff/path_parameter_diff_param_name_diff_old.yaml
@@ -0,0 +1,48 @@
+openapi: 3.0.0
+info:
+  title: Sample API
+  description: Optional multiline or single-line description in [CommonMark](http://commonmark.org/help/) or HTML.
+  version: 0.1.9
+servers:
+  - url: http://api.example.com/v1
+    description: Example api server
+
+paths:
+
+  /student/{id}:
+    get:
+      parameters:
+        - in: path
+          name: id   # Note the name is the same as in the path
+          required: true
+          schema:
+            type: string
+            minimum: 1
+
+
+      summary: Returns data about a student
+      responses:
+        '200': # status code
+          description: A JSON Object student data
+          content:
+            application/json:
+              schema:
+                type: object
+                items:
+                  $ref: '#/components/schemas/Student'
+
+components:
+  schemas:
+    Student:
+      type: object
+      properties:
+        id:
+          type: integer
+          example: "1"
+        username:
+          type: string
+          example: "alloy_horizon"
+          pattern: A..Z a..z 0..9 . _ -
+        name:
+          type: string
+          example: "Alloy Horizon"
diff --git a/core/src/test/resources/parameterDiff/path_parameter_diff_param_schema_diff_new.yaml b/core/src/test/resources/parameterDiff/path_parameter_diff_param_schema_diff_new.yaml
new file mode 100644
index 000000000..8faa0730e
--- /dev/null
+++ b/core/src/test/resources/parameterDiff/path_parameter_diff_param_schema_diff_new.yaml
@@ -0,0 +1,48 @@
+openapi: 3.0.0
+info:
+  title: Sample API
+  description: Optional multiline or single-line description in [CommonMark](http://commonmark.org/help/) or HTML.
+  version: 0.1.9
+servers:
+  - url: http://api.example.com/v1
+    description: Example api server
+
+paths:
+
+  /student/{username}:
+    get:
+      parameters:
+        - in: path
+          name: username   # Note the name is the same as in the path
+          required: true
+          schema:
+            type: string
+            minimum: 1
+
+
+      summary: Returns data about a student
+      responses:
+        '200': # status code
+          description: A JSON Object student data
+          content:
+            application/json:
+              schema:
+                type: object
+                items:
+                  $ref: '#/components/schemas/Student'
+
+components:
+  schemas:
+    Student:
+      type: object
+      properties:
+        id:
+          type: integer
+          example: "1"
+        username:
+          type: string
+          example: "alloy_horizon"
+          pattern: A..Z a..z 0..9 . _ -
+        name:
+          type: string
+          example: "Alloy Horizon"
diff --git a/core/src/test/resources/parameterDiff/path_parameter_diff_param_schema_diff_old.yaml b/core/src/test/resources/parameterDiff/path_parameter_diff_param_schema_diff_old.yaml
new file mode 100644
index 000000000..96be9b9b2
--- /dev/null
+++ b/core/src/test/resources/parameterDiff/path_parameter_diff_param_schema_diff_old.yaml
@@ -0,0 +1,48 @@
+openapi: 3.0.0
+info:
+  title: Sample API
+  description: Optional multiline or single-line description in [CommonMark](http://commonmark.org/help/) or HTML.
+  version: 0.1.9
+servers:
+  - url: http://api.example.com/v1
+    description: Example api server
+
+paths:
+
+  /student/{id}:
+    get:
+      parameters:
+        - in: path
+          name: id   # Note the name is the same as in the path
+          required: true
+          schema:
+            type: integer
+            minimum: 1
+
+
+      summary: Returns data about a student
+      responses:
+        '200': # status code
+          description: A JSON Object student data
+          content:
+            application/json:
+              schema:
+                type: object
+                items:
+                  $ref: '#/components/schemas/Student'
+
+components:
+  schemas:
+    Student:
+      type: object
+      properties:
+        id:
+          type: integer
+          example: "1"
+        username:
+          type: string
+          example: "alloy_horizon"
+          pattern: A..Z a..z 0..9 . _ -
+        name:
+          type: string
+          example: "Alloy Horizon"
diff --git a/core/src/test/resources/parameters_diff.yaml b/core/src/test/resources/parameters_diff.yaml
deleted file mode 100644
index 97cc8e9b9..000000000
--- a/core/src/test/resources/parameters_diff.yaml
+++ /dev/null
@@ -1,185 +0,0 @@
-openapi: 3.0.0
-servers:
-  - url: 'http://petstore.swagger.io/v2'
-info:
-  description: >-
-    This is a sample server Petstore server.  You can find out more about
-    Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net,
-    #swagger](http://swagger.io/irc/).  For this sample, you can use the api key
-    `special-key` to test the authorization filters.
-  version: 1.0.0
-  title: Swagger Petstore
-  termsOfService: 'http://swagger.io/terms/'
-  contact:
-    email: apiteam@swagger.io
-  license:
-    name: Apache 2.0
-    url: 'http://www.apache.org/licenses/LICENSE-2.0.html'
-tags:
-  - name: pet
-    description: Everything about your Pets
-    externalDocs:
-      description: Find out more
-      url: 'http://swagger.io'
-  - name: store
-    description: Access to Petstore orders
-  - name: user
-    description: Operations about user
-    externalDocs:
-      description: Find out more about our store
-      url: 'http://swagger.io'
-paths:
-  '/pet/{petId}':
-    parameters:
-      - name: newHeaderParam
-        in: header
-        required: false
-        schema:
-          type: integer
-    delete:
-      tags:
-        - pet
-      summary: Deletes a pet
-      description: ''
-      operationId: deletePet
-      parameters:
-        - name: api_key
-          in: header
-          required: false
-          schema:
-            type: string
-        - name: newHeaderParam
-          in: header
-          required: false
-          schema:
-            type: string
-        - name: petId
-          in: path
-          description: Pet id to delete
-          required: true
-          schema:
-            type: integer
-            format: int64
-      responses:
-        '400':
-          description: Invalid ID supplied
-        '404':
-          description: Pet not found
-  /pet:
-    post:
-      tags:
-        - pet
-      summary: Add a new pet to the store
-      description: ''
-      operationId: addPet
-      responses:
-        '405':
-          description: Invalid input
-      requestBody:
-        $ref: '#/components/requestBodies/Pet'
-  /pet/findByStatus2:
-    get:
-      tags:
-        - pet
-      summary: Finds Pets by status
-      description: Multiple status values can be provided with comma separated strings
-      operationId: findPetsByStatus
-      parameters:
-        - name: status
-          in: query
-          deprecated: true
-          description: Status values that need to be considered for filter
-          required: true
-          explode: true
-          schema:
-            type: array
-            items:
-              type: string
-              enum:
-                - available
-                - pending
-                - sold
-              default: available
-      responses:
-        '200':
-          description: successful operation
-          content:
-            application/xml:
-              schema:
-                type: array
-                items:
-                  $ref: '#/components/schemas/Pet'
-            application/json:
-              schema:
-                type: array
-                items:
-                  $ref: '#/components/schemas/Pet'
-        '400':
-          description: Invalid status value
-externalDocs:
-  description: Find out more about Swagger
-  url: 'http://swagger.io'
-components:
-  requestBodies:
-    Pet:
-      content:
-        application/json:
-          schema:
-            $ref: '#/components/schemas/Pet'
-        application/xml:
-          schema:
-            $ref: '#/components/schemas/Pet'
-      description: Pet object that needs to be added to the store
-      required: true
-  schemas:
-    Tag:
-      type: object
-      properties:
-        id:
-          type: integer
-          format: int64
-        name:
-          type: string
-      xml:
-        name: Tag
-    Pet:
-      type: object
-      required:
-        - name
-        - photoUrls
-      properties:
-        id:
-          type: integer
-          format: int64
-        category:
-          type: string
-        name:
-          type: string
-          example: doggie
-        newField:
-          type: string
-          example: a field demo
-          description: a field demo
-        photoUrls:
-          type: array
-          xml:
-            name: photoUrl
-            wrapped: true
-          items:
-            type: string
-        tags:
-          type: array
-          xml:
-            name: tag
-            wrapped: true
-          items:
-            $ref: '#/components/schemas/Tag'
-        status:
-          type: string
-          description: pet status in the store
-          enum:
-            - available
-            - pending
-            - sold
-      xml:
-        name: Pet
\ No newline at end of file
diff --git a/core/src/test/resources/parameters_diff_1.yaml b/core/src/test/resources/parameters_diff_1.yaml
deleted file mode 100644
index 97cc8e9b9..000000000
--- a/core/src/test/resources/parameters_diff_1.yaml
+++ /dev/null
@@ -1,185 +0,0 @@
-openapi: 3.0.0
-servers:
-  - url: 'http://petstore.swagger.io/v2'
-info:
-  description: >-
-    This is a sample server Petstore server.  You can find out more about
-    Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net,
-    #swagger](http://swagger.io/irc/).  For this sample, you can use the api key
-    `special-key` to test the authorization filters.
-  version: 1.0.0
-  title: Swagger Petstore
-  termsOfService: 'http://swagger.io/terms/'
-  contact:
-    email: apiteam@swagger.io
-  license:
-    name: Apache 2.0
-    url: 'http://www.apache.org/licenses/LICENSE-2.0.html'
-tags:
-  - name: pet
-    description: Everything about your Pets
-    externalDocs:
-      description: Find out more
-      url: 'http://swagger.io'
-  - name: store
-    description: Access to Petstore orders
-  - name: user
-    description: Operations about user
-    externalDocs:
-      description: Find out more about our store
-      url: 'http://swagger.io'
-paths:
-  '/pet/{petId}':
-    parameters:
-      - name: newHeaderParam
-        in: header
-        required: false
-        schema:
-          type: integer
-    delete:
-      tags:
-        - pet
-      summary: Deletes a pet
-      description: ''
-      operationId: deletePet
-      parameters:
-        - name: api_key
-          in: header
-          required: false
-          schema:
-            type: string
-        - name: newHeaderParam
-          in: header
-          required: false
-          schema:
-            type: string
-        - name: petId
-          in: path
-          description: Pet id to delete
-          required: true
-          schema:
-            type: integer
-            format: int64
-      responses:
-        '400':
-          description: Invalid ID supplied
-        '404':
-          description: Pet not found
-  /pet:
-    post:
-      tags:
-        - pet
-      summary: Add a new pet to the store
-      description: ''
-      operationId: addPet
-      responses:
-        '405':
-          description: Invalid input
-      requestBody:
-        $ref: '#/components/requestBodies/Pet'
-  /pet/findByStatus2:
-    get:
-      tags:
-        - pet
-      summary: Finds Pets by status
-      description: Multiple status values can be provided with comma separated strings
-      operationId: findPetsByStatus
-      parameters:
-        - name: status
-          in: query
-          deprecated: true
-          description: Status values that need to be considered for filter
-          required: true
-          explode: true
-          schema:
-            type: array
-            items:
-              type: string
-              enum:
-                - available
-                - pending
-                - sold
-              default: available
-      responses:
-        '200':
-          description: successful operation
-          content:
-            application/xml:
-              schema:
-                type: array
-                items:
-                  $ref: '#/components/schemas/Pet'
-            application/json:
-              schema:
-                type: array
-                items:
-                  $ref: '#/components/schemas/Pet'
-        '400':
-          description: Invalid status value
-externalDocs:
-  description: Find out more about Swagger
-  url: 'http://swagger.io'
-components:
-  requestBodies:
-    Pet:
-      content:
-        application/json:
-          schema:
-            $ref: '#/components/schemas/Pet'
-        application/xml:
-          schema:
-            $ref: '#/components/schemas/Pet'
-      description: Pet object that needs to be added to the store
-      required: true
-  schemas:
-    Tag:
-      type: object
-      properties:
-        id:
-          type: integer
-          format: int64
-        name:
-          type: string
-      xml:
-        name: Tag
-    Pet:
-      type: object
-      required:
-        - name
-        - photoUrls
-      properties:
-        id:
-          type: integer
-          format: int64
-        category:
-          type: string
-        name:
-          type: string
-          example: doggie
-        newField:
-          type: string
-          example: a field demo
-          description: a field demo
-        photoUrls:
-          type: array
-          xml:
-            name: photoUrl
-            wrapped: true
-          items:
-            type: string
-        tags:
-          type: array
-          xml:
-            name: tag
-            wrapped: true
-          items:
-            $ref: '#/components/schemas/Tag'
-        status:
-          type: string
-          description: pet status in the store
-          enum:
-            - available
-            - pending
-            - sold
-      xml:
-        name: Pet
\ No newline at end of file
diff --git a/core/src/test/resources/parameters_diff_2.yaml b/core/src/test/resources/parameters_diff_2.yaml
deleted file mode 100644
index a4e38e85d..000000000
--- a/core/src/test/resources/parameters_diff_2.yaml
+++ /dev/null
@@ -1,183 +0,0 @@
-openapi: 3.0.0
-servers:
-  - url: 'http://petstore.swagger.io/v2'
-info:
-  description: >-
-    This is a sample server Petstore server.  You can find out more about
-    Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net,
-    #swagger](http://swagger.io/irc/).  For this sample, you can use the api key
-    `special-key` to test the authorization filters.
-  version: 2.0.0
-  title: Swagger Petstore
-  termsOfService: 'http://swagger.io/terms/'
-  contact:
-    email: apiteam@swagger.io
-  license:
-    name: Apache 2.0
-    url: 'http://www.apache.org/licenses/LICENSE-2.0.html'
-tags:
-  - name: pet
-    description: Everything about your Pets
-    externalDocs:
-      description: Find out more
-      url: 'http://swagger.io'
-  - name: store
-    description: Access to Petstore orders
-  - name: user
-    description: Operations about user
-    externalDocs:
-      description: Find out more about our store
-      url: 'http://swagger.io'
-paths:
-  '/pet/{petId}':
-    delete:
-      tags:
-        - pet
-      summary: Deletes a pet
-      description: ''
-      operationId: deletePet
-      parameters:
-        - name: api_key
-          in: header
-          required: false
-          schema:
-            type: string
-        - name: petId
-          in: path
-          description: Pet id to delete
-          required: true
-          schema:
-            type: integer
-            format: int64
-      responses:
-        '400':
-          description: Invalid ID supplied
-        '404':
-          description: Pet not found
-  /pet:
-    post:
-      tags:
-        - pet
-      summary: Add a new pet to the store
-      description: ''
-      operationId: addPet
-      parameters:
-        - name: tags
-          in: query
-          description: add new query param demo
-          required: true
-          explode: true
-          schema:
-            type: array
-            items:
-              type: string
-      responses:
-        '405':
-          description: Invalid input
-      requestBody:
-        $ref: '#/components/requestBodies/Pet'
-  /pet/findByStatus2:
-    get:
-      tags:
-        - pet
-      summary: Finds Pets by status
-      description: Multiple status values can be provided with comma separated strings
-      operationId: findPetsByStatus
-      parameters:
-        - name: status
-          in: query
-          description: Status values that need to be considered for filter
-          required: true
-          explode: true
-          schema:
-            type: array
-            items:
-              type: string
-              enum:
-                - available
-                - pending
-                - sold
-              default: available
-      responses:
-        '200':
-          description: successful operation
-          content:
-            application/xml:
-              schema:
-                type: array
-                items:
-                  $ref: '#/components/schemas/Pet'
-            application/json:
-              schema:
-                type: array
-                items:
-                  $ref: '#/components/schemas/Pet'
-        '400':
-          description: Invalid status value
-externalDocs:
-  description: Find out more about Swagger
-  url: 'http://swagger.io'
-components:
-  requestBodies:
-    Pet:
-      content:
-        application/json:
-          schema:
-            $ref: '#/components/schemas/Pet'
-        application/xml:
-          schema:
-            $ref: '#/components/schemas/Pet'
-      description: Pet object that needs to be added to the store
-      required: true
-  schemas:
-    Tag:
-      type: object
-      properties:
-        id:
-          type: integer
-          format: int64
-        name:
-          type: string
-      xml:
-        name: Tag
-    Pet:
-      type: object
-      required:
-        - name
-        - photoUrls
-      properties:
-        id:
-          type: integer
-          format: int64
-        category:
-          type: string
-        name:
-          type: string
-          example: doggie
-        newField:
-          type: string
-          example: a field demo
-          description: a field demo
-        photoUrls:
-          type: array
-          xml:
-            name: photoUrl
-            wrapped: true
-          items:
-            type: string
-        tags:
-          type: array
-          xml:
-            name: tag
-            wrapped: true
-          items:
-            $ref: '#/components/schemas/Tag'
-        status:
-          type: string
-          description: pet status in the store
-          enum:
-            - available
-            - pending
-            - sold
-      xml:
-        name: Pet
\ No newline at end of file
diff --git a/core/src/test/resources/path_5.yaml b/core/src/test/resources/path_5.yaml
new file mode 100644
index 000000000..9e44371c3
--- /dev/null
+++ b/core/src/test/resources/path_5.yaml
@@ -0,0 +1,92 @@
+openapi: 3.0.0
+servers:
+  - url: 'http://petstore.swagger.io/v2'
+info:
+  description: >-
+    This is a sample server Petstore server.  You can find out more about
+    Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net,
+    #swagger](http://swagger.io/irc/).  For this sample, you can use the api key
+    `special-key` to test the authorization filters.
+  version: 1.0.0
+  title: Swagger Petstore
+  termsOfService: 'http://swagger.io/terms/'
+  contact:
+    email: apiteam@swagger.io
+  license:
+    name: Apache 2.0
+    url: 'http://www.apache.org/licenses/LICENSE-2.0.html'
+paths:
+  /pet/{petId}:
+    get:
+      tags:
+        - pet
+      summary: gets a pet by id
+      description: ''
+      operationId: updatePetWithForm
+      parameters:
+        - name: petId
+          in: path
+          description: ID of pet that needs to be updated
+          required: true
+          schema:
+            type: integer
+      responses:
+        '405':
+          description: Invalid input
+  /pet/{petId2}:
+    post:
+      tags:
+        - pet
+      summary: deletes a pet
+      description: ''
+      operationId: deletePet
+      parameters:
+        - name: petId2
+          in: path
+          description: Pet ID to delete
+          required: true
+          schema:
+            type: integer
+      responses:
+        '405':
+          description: Invalid input
+  /pet/by_owner-{ownerId}:
+    get:
+      tags:
+        - pet
+      summary: gets a pet by its owner id
+      description: ''
+      operationId: getPetFromOwner
+      parameters:
+        - ownerid: ownerId
+          in: path
+          description: Owner id of pet that needs to be get
+          required: true
+          schema:
+            type: integer
+      responses:
+        '405':
+          description: Invalid input
+  /pet/by_owner-{ownerId}_name-{petName}:
+    get:
+      tags:
+        - pet
+      summary: gets a pet by its owner id and its name
+      description: ''
+      operationId: getPetFromOwnerAndPetName
+      parameters:
+        - ownerid: ownerId
+          in: path
+          description: Owner id of pet that needs to be get
+          required: true
+          schema:
+            type: integer
+        - name: petName
+          in: path
+          description: Name of pet that needs to be get
+          required: true
+          schema:
+            type: string
+      responses:
+        '405':
+          description: Invalid input
diff --git a/core/src/test/resources/path_6.yaml b/core/src/test/resources/path_6.yaml
new file mode 100644
index 000000000..9e44371c3
--- /dev/null
+++ b/core/src/test/resources/path_6.yaml
@@ -0,0 +1,92 @@
+openapi: 3.0.0
+servers:
+  - url: 'http://petstore.swagger.io/v2'
+info:
+  description: >-
+    This is a sample server Petstore server.  You can find out more about
+    Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net,
+    #swagger](http://swagger.io/irc/).  For this sample, you can use the api key
+    `special-key` to test the authorization filters.
+  version: 1.0.0
+  title: Swagger Petstore
+  termsOfService: 'http://swagger.io/terms/'
+  contact:
+    email: apiteam@swagger.io
+  license:
+    name: Apache 2.0
+    url: 'http://www.apache.org/licenses/LICENSE-2.0.html'
+paths:
+  /pet/{petId}:
+    get:
+      tags:
+        - pet
+      summary: gets a pet by id
+      description: ''
+      operationId: updatePetWithForm
+      parameters:
+        - name: petId
+          in: path
+          description: ID of pet that needs to be updated
+          required: true
+          schema:
+            type: integer
+      responses:
+        '405':
+          description: Invalid input
+  /pet/{petId2}:
+    post:
+      tags:
+        - pet
+      summary: deletes a pet
+      description: ''
+      operationId: deletePet
+      parameters:
+        - name: petId2
+          in: path
+          description: Pet ID to delete
+          required: true
+          schema:
+            type: integer
+      responses:
+        '405':
+          description: Invalid input
+  /pet/by_owner-{ownerId}:
+    get:
+      tags:
+        - pet
+      summary: gets a pet by its owner id
+      description: ''
+      operationId: getPetFromOwner
+      parameters:
+        - ownerid: ownerId
+          in: path
+          description: Owner id of pet that needs to be get
+          required: true
+          schema:
+            type: integer
+      responses:
+        '405':
+          description: Invalid input
+  /pet/by_owner-{ownerId}_name-{petName}:
+    get:
+      tags:
+        - pet
+      summary: gets a pet by its owner id and its name
+      description: ''
+      operationId: getPetFromOwnerAndPetName
+      parameters:
+        - ownerid: ownerId
+          in: path
+          description: Owner id of pet that needs to be get
+          required: true
+          schema:
+            type: integer
+        - name: petName
+          in: path
+          description: Name of pet that needs to be get
+          required: true
+          schema:
+            type: string
+      responses:
+        '405':
+          description: Invalid input
diff --git a/core/src/test/resources/range_statuscode_1.yaml b/core/src/test/resources/range_statuscode_1.yaml
new file mode 100644
index 000000000..4653a70fc
--- /dev/null
+++ b/core/src/test/resources/range_statuscode_1.yaml
@@ -0,0 +1,10 @@
+openapi: 3.0.0
+info:
+  title: Projects API
+  version: 1.0.0
+paths:
+  /pet/:
+    get:
+      responses:
+        "405":
+          description: "Invalid input"
diff --git a/core/src/test/resources/range_statuscode_2.yaml b/core/src/test/resources/range_statuscode_2.yaml
new file mode 100644
index 000000000..d0ecbeb75
--- /dev/null
+++ b/core/src/test/resources/range_statuscode_2.yaml
@@ -0,0 +1,10 @@
+openapi: 3.0.0
+info:
+  title: Projects API
+  version: 1.0.0
+paths:
+  /pet/:
+    get:
+      responses:
+        "4XX":
+          description: "Invalid input"
diff --git a/core/src/test/resources/recursive_model_1.yaml b/core/src/test/resources/recursive_model_1.yaml
index 6a05ab0ad..24f545186 100644
--- a/core/src/test/resources/recursive_model_1.yaml
+++ b/core/src/test/resources/recursive_model_1.yaml
@@ -19,6 +19,7 @@ components:
   schemas:
     B:
       type: object
+      required: ["message2"]
       properties:
         message:
           type: string
@@ -27,4 +28,4 @@ components:
         details:
           type: array
           items:
-            $ref: '#/components/schemas/B'
\ No newline at end of file
+            $ref: '#/components/schemas/B'
diff --git a/core/src/test/resources/recursive_new.yaml b/core/src/test/resources/recursive_new.yaml
new file mode 100644
index 000000000..be1688ebc
--- /dev/null
+++ b/core/src/test/resources/recursive_new.yaml
@@ -0,0 +1,29 @@
+openapi: 3.0.1
+info:
+  title: recursive test
+  version: "1.0"
+servers:
+  - url: "http://localhost:8000/"
+paths:
+  /ping:
+    get:
+      operationId: ping
+      responses:
+        "200":
+          description: OK
+          content:
+            text/plain:
+              schema:
+                $ref: "#/components/schemas/A"
+components:
+  schemas:
+    A:
+      type: object
+      properties:
+        propname2:
+          $ref: "#/components/schemas/B"
+    B:
+      type: object
+      properties:
+        propname2:
+          $ref: "#/components/schemas/A"
\ No newline at end of file
diff --git a/core/src/test/resources/recursive_old.yaml b/core/src/test/resources/recursive_old.yaml
new file mode 100644
index 000000000..83788675c
--- /dev/null
+++ b/core/src/test/resources/recursive_old.yaml
@@ -0,0 +1,29 @@
+openapi: 3.0.1
+info:
+  title: recursive test
+  version: "1.0"
+servers:
+  - url: "http://localhost:8000/"
+paths:
+  /ping:
+    get:
+      operationId: ping
+      responses:
+        "200":
+          description: OK
+          content:
+            text/plain:
+              schema:
+                $ref: "#/components/schemas/A"
+components:
+  schemas:
+    A:
+      type: object
+      properties:
+        propname:
+          $ref: "#/components/schemas/B"
+    B:
+      type: object
+      properties:
+        propname:
+          $ref: "#/components/schemas/A"
\ No newline at end of file
diff --git a/core/src/test/resources/responseHeaderDiff/response_header_1.yaml b/core/src/test/resources/responseHeaderDiff/response_header_1.yaml
new file mode 100644
index 000000000..d668deb9a
--- /dev/null
+++ b/core/src/test/resources/responseHeaderDiff/response_header_1.yaml
@@ -0,0 +1,64 @@
+openapi: 3.1.0
+info:
+  description: Response headers handling
+  title: response headers
+  version: 1.0.0
+paths:
+  /response/headers/deprecated:
+    get:
+      responses:
+        '200':
+          description: some description
+          headers:
+            x-header-becomes-deprecated:
+              schema:
+                type: string
+            x-header-becomes-not-deprecated:
+              deprecated: true
+              schema:
+                type: string
+          content:
+            application/json: { }
+  /response/headers/description:
+    get:
+      responses:
+        '200':
+          description: some description
+          headers:
+            x-header-description-changed:
+              description: old description
+              schema:
+                type: string
+            x-header-description-added:
+              schema:
+                type: string
+            x-header-description-removed:
+              description: old description
+              schema:
+                type: string
+          content:
+            application/json: { }
+  /response/headers/required:
+    get:
+      responses:
+        '200':
+          description: some description
+          headers:
+            x-header-required-changed:
+              required: true
+              schema:
+                type: string
+          content:
+            application/json: { }
+  /response/headers/explode:
+    get:
+      responses:
+        '200':
+          description: some description
+          headers:
+            x-header-explode-changed:
+              explode: false
+              schema:
+                type: string
+          content:
+            application/json: { }
\ No newline at end of file
diff --git a/core/src/test/resources/responseHeaderDiff/response_header_2.yaml b/core/src/test/resources/responseHeaderDiff/response_header_2.yaml
new file mode 100644
index 000000000..fc2175233
--- /dev/null
+++ b/core/src/test/resources/responseHeaderDiff/response_header_2.yaml
@@ -0,0 +1,64 @@
+openapi: 3.1.0
+info:
+  description: Response headers handling
+  title: response headers
+  version: 1.0.0
+paths:
+  /response/headers/deprecated:
+    get:
+      responses:
+        '200':
+          description: some description
+          headers:
+            x-header-becomes-deprecated:
+              deprecated: true
+              schema:
+                type: string
+            x-header-becomes-not-deprecated:
+              schema:
+                type: string
+          content:
+            application/json: {}
+  /response/headers/description:
+    get:
+      responses:
+        '200':
+          description: some description
+          headers:
+            x-header-description-changed:
+              description: new description
+              schema:
+                type: string
+            x-header-description-added:
+              description: added description
+              schema:
+                type: string
+            x-header-description-removed:
+              schema:
+                type: string
+          content:
+            application/json: { }
+  /response/headers/required:
+    get:
+      responses:
+        '200':
+          description: some description
+          headers:
+            x-header-required-changed:
+              required: false
+              schema:
+                type: string
+          content:
+            application/json: { }
+  /response/headers/explode:
+    get:
+      responses:
+        '200':
+          description: some description
+          headers:
+            x-header-explode-changed:
+              explode: true
+              schema:
+                type: string
+          content:
+            application/json: { }
\ No newline at end of file
diff --git a/core/src/test/resources/add-prop-1.yaml b/core/src/test/resources/schemaDiff/schema-add-property-1.yaml
similarity index 100%
rename from core/src/test/resources/add-prop-1.yaml
rename to core/src/test/resources/schemaDiff/schema-add-property-1.yaml
diff --git a/core/src/test/resources/add-prop-2.yaml b/core/src/test/resources/schemaDiff/schema-add-property-2.yaml
similarity index 100%
rename from core/src/test/resources/add-prop-2.yaml
rename to core/src/test/resources/schemaDiff/schema-add-property-2.yaml
diff --git a/core/src/test/resources/add-prop-put-1.yaml b/core/src/test/resources/schemaDiff/schema-add-property-put-1.yaml
similarity index 100%
rename from core/src/test/resources/add-prop-put-1.yaml
rename to core/src/test/resources/schemaDiff/schema-add-property-put-1.yaml
diff --git a/core/src/test/resources/add-prop-put-2.yaml b/core/src/test/resources/schemaDiff/schema-add-property-put-2.yaml
similarity index 100%
rename from core/src/test/resources/add-prop-put-2.yaml
rename to core/src/test/resources/schemaDiff/schema-add-property-put-2.yaml
diff --git a/core/src/test/resources/issue-256_1.json b/core/src/test/resources/schemaDiff/schema-additional-properties-1.json
similarity index 100%
rename from core/src/test/resources/issue-256_1.json
rename to core/src/test/resources/schemaDiff/schema-additional-properties-1.json
diff --git a/core/src/test/resources/issue-256_2.json b/core/src/test/resources/schemaDiff/schema-additional-properties-2.json
similarity index 100%
rename from core/src/test/resources/issue-256_2.json
rename to core/src/test/resources/schemaDiff/schema-additional-properties-2.json
diff --git a/core/src/test/resources/schemaDiff/schema-deprecated-handling-1.yaml b/core/src/test/resources/schemaDiff/schema-deprecated-handling-1.yaml
new file mode 100644
index 000000000..48dcd021e
--- /dev/null
+++ b/core/src/test/resources/schemaDiff/schema-deprecated-handling-1.yaml
@@ -0,0 +1,33 @@
+openapi: 3.1.0
+info:
+  description: Schema deprecated handling
+  title: defaults
+  version: 1.0.0
+paths:
+  /schema-diff/deprecated/added:
+    post:
+      requestBody:
+        content:
+          application/json:
+            schema:
+              $ref: "#/components/schemas/DeprecatedAdded"
+  /schema-diff/deprecated/removed:
+    post:
+      requestBody:
+        content:
+          application/json:
+            schema:
+              $ref: "#/components/schemas/DeprecatedRemoved"
+components:
+  schemas:
+    DeprecatedAdded:
+      type: object
+      properties:
+        field1:
+          type: string
+    DeprecatedRemoved:
+      type: object
+      deprecated: true
+      properties:
+        field1:
+          type: string
\ No newline at end of file
diff --git a/core/src/test/resources/schemaDiff/schema-deprecated-handling-2.yaml b/core/src/test/resources/schemaDiff/schema-deprecated-handling-2.yaml
new file mode 100644
index 000000000..00432216c
--- /dev/null
+++ b/core/src/test/resources/schemaDiff/schema-deprecated-handling-2.yaml
@@ -0,0 +1,33 @@
+openapi: 3.1.0
+info:
+  description: Schema deprecated handling
+  title: defaults
+  version: 1.0.0
+paths:
+  /schema-diff/deprecated/added:
+    post:
+      requestBody:
+        content:
+          application/json:
+            schema:
+              $ref: "#/components/schemas/DeprecatedAdded"
+  /schema-diff/deprecated/removed:
+    post:
+      requestBody:
+        content:
+          application/json:
+            schema:
+              $ref: "#/components/schemas/DeprecatedRemoved"
+components:
+  schemas:
+    DeprecatedAdded:
+      deprecated: true
+      type: object
+      properties:
+        field1:
+          type: string
+    DeprecatedRemoved:
+      type: object
+      properties:
+        field1:
+          type: string
\ No newline at end of file
diff --git a/core/src/test/resources/schemaDiff/schema-min-max-items-diff-1.yaml b/core/src/test/resources/schemaDiff/schema-min-max-items-diff-1.yaml
new file mode 100644
index 000000000..23d841484
--- /dev/null
+++ b/core/src/test/resources/schemaDiff/schema-min-max-items-diff-1.yaml
@@ -0,0 +1,42 @@
+openapi: 3.1.0
+info:
+  description: Schema diff
+  title: schema diff
+  version: 1.0.0
+paths:
+  /schema/array/items:
+    post:
+      requestBody:
+        content:
+          application/json:
+            schema:
+              $ref: '#/components/schemas/TestDTO'
+components:
+  schemas:
+    TestDTO:
+      type: object
+      properties:
+        field1:
+          type: array
+          items:
+            type: string
+          minItems: 1
+          maxItems: 100
+        field2:
+          type: array
+          items:
+            type: string
+          minItems: 20
+          maxItems: 100
+        field3:
+          type: array
+          items:
+            type: string
+          minItems: 1
+          maxItems: 90              
+        field4:
+          type: array
+          items:
+            type: string
+          minItems: 1
+          maxItems: 100
diff --git a/core/src/test/resources/schemaDiff/schema-min-max-items-diff-2.yaml b/core/src/test/resources/schemaDiff/schema-min-max-items-diff-2.yaml
new file mode 100644
index 000000000..0d8e6199b
--- /dev/null
+++ b/core/src/test/resources/schemaDiff/schema-min-max-items-diff-2.yaml
@@ -0,0 +1,42 @@
+openapi: 3.1.0
+info:
+  description: Schema diff
+  title: schema diff
+  version: 1.0.0
+paths:
+  /schema/array/items:
+    post:
+      requestBody:
+        content:
+          application/json:
+            schema:
+              $ref: '#/components/schemas/TestDTO'
+components:
+  schemas:
+    TestDTO:
+      type: object
+      properties:
+        field1:
+          type: array
+          items:
+            type: string
+          minItems: 2
+          maxItems: 100
+        field2:
+          type: array
+          items:
+            type: string
+          minItems: 10
+          maxItems: 100  
+        field3:
+          type: array
+          items:
+            type: string
+          minItems: 1
+          maxItems: 100  
+        field4:
+          type: array
+          items:
+            type: string
+          minItems: 1
+          maxItems: 90
diff --git a/core/src/test/resources/schemaDiff/schema-min-max-properties-diff-1.yaml b/core/src/test/resources/schemaDiff/schema-min-max-properties-diff-1.yaml
new file mode 100644
index 000000000..263d710f7
--- /dev/null
+++ b/core/src/test/resources/schemaDiff/schema-min-max-properties-diff-1.yaml
@@ -0,0 +1,30 @@
+openapi: 3.0.1
+info:
+  description: Schema diff
+  title: schema diff
+  version: 1.0.0
+paths:
+  /schema/object/min-max-properties:
+    post:
+      requestBody:
+        content:
+          application/json:
+            schema:
+              $ref: '#/components/schemas/TestDTO'
+components:
+  schemas:
+    TestDTO:
+      type: object
+      properties:
+        field1:
+          type: object
+          minProperties: 1
+        field2:
+          type: object
+          minProperties: 10
+        field3:
+          type: object
+          maxProperties: 10
+        field4:
+          type: object
+          maxProperties: 100
diff --git a/core/src/test/resources/schemaDiff/schema-min-max-properties-diff-2.yaml b/core/src/test/resources/schemaDiff/schema-min-max-properties-diff-2.yaml
new file mode 100644
index 000000000..17183d8a5
--- /dev/null
+++ b/core/src/test/resources/schemaDiff/schema-min-max-properties-diff-2.yaml
@@ -0,0 +1,30 @@
+openapi: 3.0.1
+info:
+  description: Schema diff
+  title: schema diff
+  version: 1.0.0
+paths:
+  /schema/object/min-max-properties:
+    post:
+      requestBody:
+        content:
+          application/json:
+            schema:
+              $ref: '#/components/schemas/TestDTO'
+components:
+  schemas:
+    TestDTO:
+      type: object
+      properties:
+        field1:
+          type: object
+          minProperties: 10
+        field2:
+          type: object
+          minProperties: 1
+        field3:
+          type: object
+          maxProperties: 100
+        field4:
+          type: object
+          maxProperties: 10
diff --git a/core/src/test/resources/schemaDiff/schema-multiple-of-diff-1.yaml b/core/src/test/resources/schemaDiff/schema-multiple-of-diff-1.yaml
new file mode 100644
index 000000000..49d3f3b17
--- /dev/null
+++ b/core/src/test/resources/schemaDiff/schema-multiple-of-diff-1.yaml
@@ -0,0 +1,29 @@
+openapi: 3.1.0
+info:
+  description: Schema diff
+  title: schema diff
+  version: 1.0.0
+paths:
+  /schema/numeric/multiple-of:
+    post:
+      requestBody:
+        content:
+          application/json:
+            schema:
+              $ref: '#/components/schemas/TestDTO'
+components:
+  schemas:
+    TestDTO:
+      type: object
+      properties:
+        field1:
+          type: integer
+          multipleOf: 10
+        field2:
+          type: integer
+          multipleOf: 0.01
+        field3:
+          type: integer
+        field4:
+          type: integer
+          multipleOf: 10 
\ No newline at end of file
diff --git a/core/src/test/resources/schemaDiff/schema-multiple-of-diff-2.yaml b/core/src/test/resources/schemaDiff/schema-multiple-of-diff-2.yaml
new file mode 100644
index 000000000..58311d082
--- /dev/null
+++ b/core/src/test/resources/schemaDiff/schema-multiple-of-diff-2.yaml
@@ -0,0 +1,29 @@
+openapi: 3.1.0
+info:
+  description: Schema diff
+  title: schema diff
+  version: 1.0.0
+paths:
+  /schema/numeric/multiple-of:
+    post:
+      requestBody:
+        content:
+          application/json:
+            schema:
+              $ref: '#/components/schemas/TestDTO'
+components:
+  schemas:
+    TestDTO:
+      type: object
+      properties:
+        field1:
+          type: integer
+          multipleOf: 20
+        field2:
+          type: integer
+          multipleOf: 0.1
+        field3:
+          type: integer
+          multipleOf: 10 
+        field4:
+          type: integer           
\ No newline at end of file
diff --git a/core/src/test/resources/schemaDiff/schema-nullable-diff-1.yaml b/core/src/test/resources/schemaDiff/schema-nullable-diff-1.yaml
new file mode 100644
index 000000000..b467c3746
--- /dev/null
+++ b/core/src/test/resources/schemaDiff/schema-nullable-diff-1.yaml
@@ -0,0 +1,28 @@
+openapi: 3.0.1
+info:
+  description: Schema diff nullable
+  title: schema diff nullable
+  version: 1.0.0
+paths:
+  /schema/nullable:
+    post:
+      requestBody:
+        content:
+          application/json:
+            schema:
+              $ref: '#/components/schemas/TestDTO'
+components:
+  schemas:
+    TestDTO:
+      type: object
+      properties:
+        field0:
+          type: integer
+        field1:
+          type: integer
+          nullable: true
+        field2:
+          type: integer
+          nullable: true
+        field3:
+          type: integer
\ No newline at end of file
diff --git a/core/src/test/resources/schemaDiff/schema-nullable-diff-2.yaml b/core/src/test/resources/schemaDiff/schema-nullable-diff-2.yaml
new file mode 100644
index 000000000..e186f76e6
--- /dev/null
+++ b/core/src/test/resources/schemaDiff/schema-nullable-diff-2.yaml
@@ -0,0 +1,28 @@
+openapi: 3.0.1
+info:
+  description: Schema diff nullable
+  title: schema diff nullable
+  version: 1.0.0
+paths:
+  /schema/nullable:
+    post:
+      requestBody:
+        content:
+          application/json:
+            schema:
+              $ref: '#/components/schemas/TestDTO'
+components:
+  schemas:
+    TestDTO:
+      type: object
+      properties:
+        field0:
+          type: integer
+        field1:
+          type: integer
+          nullable: false
+        field2:
+          type: integer
+        field3:
+          type: integer
+          nullable: true
\ No newline at end of file
diff --git a/core/src/test/resources/schemaDiff/schema-props-defaults-handling-1.yaml b/core/src/test/resources/schemaDiff/schema-props-defaults-handling-1.yaml
new file mode 100644
index 000000000..3ecee6886
--- /dev/null
+++ b/core/src/test/resources/schemaDiff/schema-props-defaults-handling-1.yaml
@@ -0,0 +1,21 @@
+openapi: 3.1.0
+info:
+  description: Schema defaults handling
+  title: defaults
+  version: 1.0.0
+paths:
+  /defaults/property-schema/:
+    post:
+      requestBody:
+        content:
+          application/json:
+            schema:
+              $ref: '#/components/schemas/TestDTO'
+components:
+  schemas:
+    TestDTO:
+      type: object
+      properties:
+        field1:
+          default: default value
+          type: string
\ No newline at end of file
diff --git a/core/src/test/resources/schemaDiff/schema-props-defaults-handling-2.yaml b/core/src/test/resources/schemaDiff/schema-props-defaults-handling-2.yaml
new file mode 100644
index 000000000..143174770
--- /dev/null
+++ b/core/src/test/resources/schemaDiff/schema-props-defaults-handling-2.yaml
@@ -0,0 +1,21 @@
+openapi: 3.1.0
+info:
+  description: Schema defaults handling
+  title: defaults
+  version: 1.0.0
+paths:
+  /defaults/property-schema/:
+    post:
+      requestBody:
+        content:
+          application/json:
+            schema:
+              $ref: '#/components/schemas/TestDTO'
+components:
+  schemas:
+    TestDTO:
+      type: object
+      properties:
+        field1:
+          default: default value updated
+          type: string
\ No newline at end of file
diff --git a/core/src/test/resources/schemaDiff/schema-uniqueItems-diff-1.yaml b/core/src/test/resources/schemaDiff/schema-uniqueItems-diff-1.yaml
new file mode 100644
index 000000000..dc0e60dec
--- /dev/null
+++ b/core/src/test/resources/schemaDiff/schema-uniqueItems-diff-1.yaml
@@ -0,0 +1,31 @@
+openapi: 3.0.1
+info:
+  description: Schema diff uniqueItems
+  title: schema diff uniqueItems
+  version: 1.0.0
+paths:
+  /schema/uniqueItems:
+    post:
+      requestBody:
+        content:
+          application/json:
+            schema:
+              $ref: '#/components/schemas/TestDTO'
+components:
+  schemas:
+    TestDTO:
+      type: object
+      properties:
+        field0:
+          type: integer
+        field1:
+          type: integer
+          uniqueItems: true
+        field2:
+          type: integer
+          uniqueItems: false
+        field3:
+          type: integer
+          uniqueItems: true
+        field4:
+          type: integer
\ No newline at end of file
diff --git a/core/src/test/resources/schemaDiff/schema-uniqueItems-diff-2.yaml b/core/src/test/resources/schemaDiff/schema-uniqueItems-diff-2.yaml
new file mode 100644
index 000000000..12e2b906e
--- /dev/null
+++ b/core/src/test/resources/schemaDiff/schema-uniqueItems-diff-2.yaml
@@ -0,0 +1,31 @@
+openapi: 3.0.1
+info:
+  description: Schema diff uniqueItems
+  title: schema diff uniqueItems
+  version: 1.0.0
+paths:
+  /schema/uniqueItems:
+    post:
+      requestBody:
+        content:
+          application/json:
+            schema:
+              $ref: '#/components/schemas/TestDTO'
+components:
+  schemas:
+    TestDTO:
+      type: object
+      properties:
+        field0:
+          type: integer
+        field1:
+          type: integer
+          uniqueItems: false
+        field2:
+          type: integer
+          uniqueItems: true
+        field3:
+          type: integer
+        field4:
+          type: integer
+          uniqueItems: true
\ No newline at end of file
diff --git a/maven-example/pom.xml b/maven-example/pom.xml
index 7ec2fcc2d..356a10e0e 100644
--- a/maven-example/pom.xml
+++ b/maven-example/pom.xml
@@ -3,7 +3,7 @@
     <parent>
         <artifactId>openapi-diff-parent</artifactId>
         <groupId>org.openapitools.openapidiff</groupId>
-        <version>2.0.1-SNAPSHOT</version>
+        <version>2.1.0-SNAPSHOT</version>
     </parent>
     <modelVersion>4.0.0</modelVersion>
 
@@ -23,7 +23,7 @@
             <plugin>
                 <groupId>org.openapitools.openapidiff</groupId>
                 <artifactId>openapi-diff-maven</artifactId>
-                <version>2.0.1-SNAPSHOT</version>
+                <version>2.1.0-SNAPSHOT</version>
                 <executions>
                     <execution>
                         <goals>
@@ -33,6 +33,15 @@
                             <oldSpec>${project.basedir}/../maven/src/test/resources/oldspec.yaml</oldSpec>
                             <newSpec>${project.basedir}/../maven/src/test/resources/newspec.yaml</newSpec>
                             <failOnIncompatible>true</failOnIncompatible>
+                            <consoleOutputFileName>${project.basedir}/../maven/target/diff.txt</consoleOutputFileName>
+                            <jsonOutputFileName>${project.basedir}/../maven/target/diff.json</jsonOutputFileName>
+                            <markdownOutputFileName>${project.basedir}/../maven/target/diff.md</markdownOutputFileName>
+                            <configFiles>
+                                <configFile>${project.basedir}/../maven/src/test/resources/test-config-file.yaml</configFile>
+                            </configFiles>
+                            <configProps>
+                                <incompatible.request.params.required.increased>false</incompatible.request.params.required.increased>
+                            </configProps>
                         </configuration>
                     </execution>
                 </executions>
diff --git a/maven/pom.xml b/maven/pom.xml
index ed52e83c0..afaa97de4 100644
--- a/maven/pom.xml
+++ b/maven/pom.xml
@@ -5,7 +5,7 @@
   <parent>
     <groupId>org.openapitools.openapidiff</groupId>
     <artifactId>openapi-diff-parent</artifactId>
-    <version>2.0.1-SNAPSHOT</version>
+    <version>2.1.0-SNAPSHOT</version>
   </parent>
 
   <artifactId>openapi-diff-maven</artifactId>
@@ -43,6 +43,11 @@
       <artifactId>junit-jupiter</artifactId>
       <scope>test</scope>
     </dependency>
+    <dependency>
+      <groupId>org.slf4j</groupId>
+      <artifactId>slf4j-simple</artifactId>
+      <scope>test</scope>
+    </dependency>
   </dependencies>
 
   <build>
@@ -51,12 +56,12 @@
         <plugin>
           <groupId>org.apache.maven.plugins</groupId>
           <artifactId>maven-plugin-plugin</artifactId>
-          <version>3.6.2</version>
+          <version>3.8.1</version>
         </plugin>
         <plugin>
           <groupId>org.apache.maven.plugins</groupId>
           <artifactId>maven-site-plugin</artifactId>
-          <version>3.9.1</version>
+          <version>3.21.0</version>
         </plugin>
       </plugins>
     </pluginManagement>
diff --git a/maven/src/main/java/org/openapitools/openapidiff/maven/OpenApiDiffMojo.java b/maven/src/main/java/org/openapitools/openapidiff/maven/OpenApiDiffMojo.java
index 900410420..4fd7d9e14 100644
--- a/maven/src/main/java/org/openapitools/openapidiff/maven/OpenApiDiffMojo.java
+++ b/maven/src/main/java/org/openapitools/openapidiff/maven/OpenApiDiffMojo.java
@@ -1,5 +1,14 @@
 package org.openapitools.openapidiff.maven;
 
+import static org.openapitools.openapidiff.core.utils.FileUtils.writeToFile;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.io.UncheckedIOException;
+import java.util.List;
+import java.util.Map;
 import org.apache.maven.plugin.AbstractMojo;
 import org.apache.maven.plugin.MojoExecutionException;
 import org.apache.maven.plugin.MojoFailureException;
@@ -7,12 +16,17 @@
 import org.apache.maven.plugins.annotations.Mojo;
 import org.apache.maven.plugins.annotations.Parameter;
 import org.openapitools.openapidiff.core.OpenApiCompare;
+import org.openapitools.openapidiff.core.compare.OpenApiDiffOptions;
 import org.openapitools.openapidiff.core.model.ChangedOpenApi;
+import org.openapitools.openapidiff.core.output.AsciidocRender;
 import org.openapitools.openapidiff.core.output.ConsoleRender;
+import org.openapitools.openapidiff.core.output.JsonRender;
+import org.openapitools.openapidiff.core.output.MarkdownRender;
 
 /** A Maven Mojo that diffs two OpenAPI specifications and reports on differences. */
 @Mojo(name = "diff", defaultPhase = LifecyclePhase.TEST)
 public class OpenApiDiffMojo extends AbstractMojo {
+
   @Parameter(property = "oldSpec")
   String oldSpec;
 
@@ -25,11 +39,58 @@ public class OpenApiDiffMojo extends AbstractMojo {
   @Parameter(property = "failOnChanged", defaultValue = "false")
   Boolean failOnChanged = false;
 
+  @Parameter(property = "skip", defaultValue = "false")
+  Boolean skip = false;
+
+  @Parameter(property = "consoleOutputFileName")
+  String consoleOutputFileName;
+
+  @Parameter(property = "jsonOutputFileName")
+  String jsonOutputFileName;
+
+  @Parameter(property = "markdownOutputFileName")
+  String markdownOutputFileName;
+
+  @Parameter(property = "configFiles")
+  List<File> configFiles;
+
+  @Parameter(property = "configProps")
+  Map<String, String> configProps;
+
+  @Parameter(property = "asciidocOutputFileName")
+  String asciidocOutputFileName;
+
   @Override
   public void execute() throws MojoExecutionException, MojoFailureException {
+    if (Boolean.TRUE.equals(skip)) {
+      getLog().info("Skipping openapi-diff execution");
+      return;
+    }
+
     try {
-      final ChangedOpenApi diff = OpenApiCompare.fromLocations(oldSpec, newSpec);
-      getLog().info(new ConsoleRender().render(diff));
+      OpenApiDiffOptions.Builder optionBuilder = OpenApiDiffOptions.builder();
+      if (configFiles != null) {
+        configFiles.forEach(optionBuilder::configYaml);
+      }
+      if (configProps != null) {
+        configProps.forEach(optionBuilder::configProperty);
+      }
+      OpenApiDiffOptions options = optionBuilder.build();
+
+      final ChangedOpenApi diff = OpenApiCompare.fromLocations(oldSpec, newSpec, null, options);
+
+      try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+          OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream)) {
+        new ConsoleRender().render(diff, outputStreamWriter);
+        getLog().info(outputStream.toString());
+      } catch (IOException e) {
+        throw new UncheckedIOException(e);
+      }
+
+      writeDiffAsTextToFile(diff);
+      writeDiffAsJsonToFile(diff);
+      writeDiffAsMarkdownToFile(diff);
+      writeDiffAsAsciidocToFile(diff);
 
       if (failOnIncompatible && diff.isIncompatible()) {
         throw new BackwardIncompatibilityException("The API changes broke backward compatibility");
@@ -42,4 +103,20 @@ public void execute() throws MojoExecutionException, MojoFailureException {
       throw new MojoExecutionException("Unexpected error", e);
     }
   }
+
+  private void writeDiffAsTextToFile(final ChangedOpenApi diff) {
+    writeToFile(new ConsoleRender(), diff, consoleOutputFileName);
+  }
+
+  private void writeDiffAsJsonToFile(final ChangedOpenApi diff) {
+    writeToFile(new JsonRender(), diff, jsonOutputFileName);
+  }
+
+  private void writeDiffAsMarkdownToFile(final ChangedOpenApi diff) {
+    writeToFile(new MarkdownRender(), diff, markdownOutputFileName);
+  }
+
+  private void writeDiffAsAsciidocToFile(final ChangedOpenApi diff) {
+    writeToFile(new AsciidocRender(), diff, asciidocOutputFileName);
+  }
 }
diff --git a/maven/src/test/java/org/openapitools/openapidiff/maven/OpenApiDiffMojoTest.java b/maven/src/test/java/org/openapitools/openapidiff/maven/OpenApiDiffMojoTest.java
index 5ecc457e5..d38c66bde 100644
--- a/maven/src/test/java/org/openapitools/openapidiff/maven/OpenApiDiffMojoTest.java
+++ b/maven/src/test/java/org/openapitools/openapidiff/maven/OpenApiDiffMojoTest.java
@@ -1,15 +1,47 @@
 package org.openapitools.openapidiff.maven;
 
-import static org.junit.jupiter.api.Assertions.*;
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
 
 import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.Collections;
 import org.apache.maven.plugin.MojoExecutionException;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 class OpenApiDiffMojoTest {
+
+  private static final Logger logger = LoggerFactory.getLogger(OpenApiDiffMojoTest.class);
+
+  private final File oldSpecFile = new File("src/test/resources/oldspec.yaml");
+  private final File newSpecFile = new File("src/test/resources/newspec.yaml");
+  private final File configFile = new File("src/test/resources/test-config-file.yaml");
+
+  private final File consoleOutputfile = new File("target/diff.txt");
+  private final File markdownOutputfile = new File("target/diff.md");
+  private final File jsonOutputfile = new File("target/diff.json");
+  private final File asciidocOutputfile = new File("target/diff.adoc");
+
+  @BeforeEach
+  void setup() {
+    cleanupGeneratedFiles();
+  }
+
+  @AfterEach
+  void tearDown() {
+    cleanupGeneratedFiles();
+  }
+
   @Test
   void Should_NotThrow_When_SpecHasNoChanges() {
-    final String oldSpec = new File("src/test/resources/oldspec.yaml").getAbsolutePath();
+    final String oldSpec = oldSpecFile.getAbsolutePath();
 
     final OpenApiDiffMojo mojo = new OpenApiDiffMojo();
     mojo.oldSpec = oldSpec;
@@ -22,8 +54,8 @@ void Should_NotThrow_When_SpecHasNoChanges() {
   @Test
   void Should_NotThrow_When_SpecIsCompatible() {
     final OpenApiDiffMojo mojo = new OpenApiDiffMojo();
-    mojo.oldSpec = new File("src/test/resources/oldspec.yaml").getAbsolutePath();
-    mojo.newSpec = new File("src/test/resources/newspec.yaml").getAbsolutePath();
+    mojo.oldSpec = oldSpecFile.getAbsolutePath();
+    mojo.newSpec = newSpecFile.getAbsolutePath();
     mojo.failOnIncompatible = true;
 
     assertDoesNotThrow(mojo::execute);
@@ -32,8 +64,8 @@ void Should_NotThrow_When_SpecIsCompatible() {
   @Test
   void Should_Throw_When_SpecIsDifferent() {
     final OpenApiDiffMojo mojo = new OpenApiDiffMojo();
-    mojo.oldSpec = new File("src/test/resources/oldspec.yaml").getAbsolutePath();
-    mojo.newSpec = new File("src/test/resources/newspec.yaml").getAbsolutePath();
+    mojo.oldSpec = oldSpecFile.getAbsolutePath();
+    mojo.newSpec = newSpecFile.getAbsolutePath();
     mojo.failOnChanged = true;
 
     assertThrows(ApiChangedException.class, mojo::execute);
@@ -43,7 +75,7 @@ void Should_Throw_When_SpecIsDifferent() {
   void Should_MojoExecutionException_When_MissingOldSpec() {
     final OpenApiDiffMojo mojo = new OpenApiDiffMojo();
     mojo.oldSpec = new File("DOES_NOT_EXIST").getAbsolutePath();
-    mojo.newSpec = new File("src/test/resources/newspec.yaml").getAbsolutePath();
+    mojo.newSpec = newSpecFile.getAbsolutePath();
 
     assertThrows(MojoExecutionException.class, mojo::execute);
   }
@@ -51,7 +83,7 @@ void Should_MojoExecutionException_When_MissingOldSpec() {
   @Test
   void Should_MojoExecutionException_When_MissingNewSpec() {
     final OpenApiDiffMojo mojo = new OpenApiDiffMojo();
-    mojo.oldSpec = new File("src/test/resources/oldspec.yaml").getAbsolutePath();
+    mojo.oldSpec = oldSpecFile.getAbsolutePath();
     mojo.newSpec = new File("DOES_NOT_EXIST").getAbsolutePath();
 
     assertThrows(MojoExecutionException.class, mojo::execute);
@@ -60,8 +92,8 @@ void Should_MojoExecutionException_When_MissingNewSpec() {
   @Test
   void Should_NotThrow_When_DefaultsAndSpecIsIncompatible() {
     final OpenApiDiffMojo mojo = new OpenApiDiffMojo();
-    mojo.oldSpec = new File("src/test/resources/newspec.yaml").getAbsolutePath();
-    mojo.newSpec = new File("src/test/resources/oldspec.yaml").getAbsolutePath();
+    mojo.oldSpec = newSpecFile.getAbsolutePath();
+    mojo.newSpec = oldSpecFile.getAbsolutePath();
 
     assertDoesNotThrow(mojo::execute);
   }
@@ -69,10 +101,110 @@ void Should_NotThrow_When_DefaultsAndSpecIsIncompatible() {
   @Test
   void Should_BackwardIncompatibilityException_When_WantsExceptionAndSpecIsIncompatible() {
     final OpenApiDiffMojo mojo = new OpenApiDiffMojo();
-    mojo.oldSpec = new File("src/test/resources/newspec.yaml").getAbsolutePath();
-    mojo.newSpec = new File("src/test/resources/oldspec.yaml").getAbsolutePath();
+    mojo.oldSpec = newSpecFile.getAbsolutePath();
+    mojo.newSpec = oldSpecFile.getAbsolutePath();
     mojo.failOnIncompatible = true;
 
     assertThrows(BackwardIncompatibilityException.class, mojo::execute);
   }
+
+  @Test
+  void Should_NotThrow_When_ConfigWithIncompatibleChecksDisabled() {
+    final OpenApiDiffMojo mojo = new OpenApiDiffMojo();
+    mojo.oldSpec = newSpecFile.getAbsolutePath();
+    mojo.newSpec = oldSpecFile.getAbsolutePath();
+    mojo.failOnIncompatible = true;
+    mojo.configFiles = Collections.singletonList(configFile);
+    mojo.configProps =
+        Collections.singletonMap("incompatible.request.params.required.increased", "false");
+
+    assertDoesNotThrow(mojo::execute);
+  }
+
+  @Test
+  void Should_Skip_Mojo_WhenSkipIsTrue() {
+    final OpenApiDiffMojo mojo = new OpenApiDiffMojo();
+    mojo.oldSpec = newSpecFile.getAbsolutePath();
+    mojo.newSpec = oldSpecFile.getAbsolutePath();
+    mojo.failOnIncompatible = true;
+    mojo.skip = true;
+
+    assertDoesNotThrow(mojo::execute);
+  }
+
+  @Test
+  void Should_outputToTextFile_When_SpecIsDifferent() {
+    final OpenApiDiffMojo mojo = new OpenApiDiffMojo();
+    mojo.oldSpec = oldSpecFile.getAbsolutePath();
+    mojo.newSpec = newSpecFile.getAbsolutePath();
+
+    mojo.consoleOutputFileName = consoleOutputfile.getAbsolutePath();
+    mojo.failOnChanged = true;
+
+    assertThrows(ApiChangedException.class, mojo::execute);
+
+    assertTrue(Files.exists(consoleOutputfile.toPath()));
+  }
+
+  @Test
+  void Should_outputToMarkdownFile_When_SpecIsDifferent() {
+    final OpenApiDiffMojo mojo = new OpenApiDiffMojo();
+    mojo.oldSpec = oldSpecFile.getAbsolutePath();
+    mojo.newSpec = newSpecFile.getAbsolutePath();
+
+    mojo.markdownOutputFileName = markdownOutputfile.getAbsolutePath();
+    mojo.failOnChanged = true;
+
+    assertThrows(ApiChangedException.class, mojo::execute);
+
+    assertTrue(Files.exists(markdownOutputfile.toPath()));
+  }
+
+  @Test
+  void Should_outputToJsonFile_When_SpecIsDifferent() {
+    final OpenApiDiffMojo mojo = new OpenApiDiffMojo();
+    mojo.oldSpec = oldSpecFile.getAbsolutePath();
+    mojo.newSpec = newSpecFile.getAbsolutePath();
+
+    mojo.jsonOutputFileName = jsonOutputfile.getAbsolutePath();
+    mojo.failOnChanged = true;
+
+    assertThrows(ApiChangedException.class, mojo::execute);
+
+    assertTrue(Files.exists(jsonOutputfile.toPath()));
+  }
+
+  @Test
+  void Should_outputToAsccidocFile_When_SpecIsDifferent() {
+    final OpenApiDiffMojo mojo = new OpenApiDiffMojo();
+    mojo.oldSpec = oldSpecFile.getAbsolutePath();
+    mojo.newSpec = newSpecFile.getAbsolutePath();
+
+    mojo.asciidocOutputFileName = asciidocOutputfile.getAbsolutePath();
+    mojo.failOnChanged = true;
+
+    assertThrows(ApiChangedException.class, mojo::execute);
+
+    assertTrue(Files.exists(asciidocOutputfile.toPath()));
+  }
+
+  private void cleanupGeneratedFiles() {
+    try {
+      Files.deleteIfExists(Paths.get(consoleOutputfile.getPath()));
+    } catch (IOException ioException) {
+      logger.warn("Exception while trying to delete file {}", consoleOutputfile.getAbsolutePath());
+    }
+
+    try {
+      Files.deleteIfExists(Paths.get(markdownOutputfile.getPath()));
+    } catch (IOException ioException) {
+      logger.warn("Exception while trying to delete file {}", markdownOutputfile.getAbsolutePath());
+    }
+
+    try {
+      Files.deleteIfExists(Paths.get(jsonOutputfile.getPath()));
+    } catch (IOException ioException) {
+      logger.warn("Exception while trying to delete file {}", jsonOutputfile.getAbsolutePath());
+    }
+  }
 }
diff --git a/maven/src/test/resources/logback.xml b/maven/src/test/resources/logback.xml
new file mode 100644
index 000000000..018a4277c
--- /dev/null
+++ b/maven/src/test/resources/logback.xml
@@ -0,0 +1,11 @@
+<configuration>
+  <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
+    <encoder>
+      <pattern>[%thread] %-5level %logger{36} - %msg%n</pattern>
+    </encoder>
+  </appender>
+
+  <root level="debug">
+    <appender-ref ref="STDOUT"/>
+  </root>
+</configuration>
\ No newline at end of file
diff --git a/maven/src/test/resources/newspec.yaml b/maven/src/test/resources/newspec.yaml
index e95978f04..7d326b882 100644
--- a/maven/src/test/resources/newspec.yaml
+++ b/maven/src/test/resources/newspec.yaml
@@ -1,15 +1,33 @@
----
-openapi: 3.0.3
+openapi: 3.0.0
 info:
-  title: Generated API
-  version: "1.0"
+  description: myDesc
+  title: myTitle
+  version: 1.0.0
 paths:
-  /hello:
+  /widgets:
     get:
+      operationId: listWidgets
+      parameters:
+        - name: query-param-1
+          in: query
+          schema:
+            type: string
+        - name: query-param-2
+          in: query
+          schema:
+            type: string
       responses:
-        "200":
-          description: OK
+        '200':
+          description: successful operation
           content:
-            text/plain:
+            application/json:
               schema:
-                type: string
+                type: object
+                properties:
+                  prop1:
+                    type: string
+                  prop2:
+                    type: string
+                required:
+                  - prop1
+                  - prop2
diff --git a/maven/src/test/resources/oldspec.yaml b/maven/src/test/resources/oldspec.yaml
index 2f2735763..40cd4175e 100644
--- a/maven/src/test/resources/oldspec.yaml
+++ b/maven/src/test/resources/oldspec.yaml
@@ -1,6 +1,33 @@
----
-openapi: 3.0.3
+openapi: 3.0.0
 info:
-  title: Generated API
-  version: "1.0"
-paths: {}
+  description: myDesc
+  title: myTitle
+  version: 1.0.0
+paths:
+  /widgets:
+    get:
+      operationId: listWidgets
+      parameters:
+        - name: query-param-1
+          in: query
+          required: true
+          schema:
+            type: string
+        - name: query-param-2
+          in: query
+          schema:
+            type: string
+      responses:
+        '200':
+          description: successful operation
+          content:
+            application/json:
+              schema:
+                type: object
+                properties:
+                  prop1:
+                    type: string
+                  prop2:
+                    type: string
+                required:
+                  - prop1
diff --git a/maven/src/test/resources/test-config-file.yaml b/maven/src/test/resources/test-config-file.yaml
new file mode 100644
index 000000000..633ad0be3
--- /dev/null
+++ b/maven/src/test/resources/test-config-file.yaml
@@ -0,0 +1,8 @@
+####
+## Config file for openapi-diff
+####
+
+incompatible:
+  response:
+    required:
+      decreased: false
diff --git a/mvnw b/mvnw
index fc7efd17d..b7f064624 100755
--- a/mvnw
+++ b/mvnw
@@ -19,7 +19,7 @@
 # ----------------------------------------------------------------------------
 
 # ----------------------------------------------------------------------------
-# Maven2 Start Up Batch script
+# Apache Maven Wrapper startup batch script, version 3.1.1
 #
 # Required ENV vars:
 # ------------------
@@ -27,7 +27,6 @@
 #
 # Optional ENV vars
 # -----------------
-#   M2_HOME - location of maven2's installed home dir
 #   MAVEN_OPTS - parameters passed to the Java VM when running Maven
 #     e.g. to debug Maven itself, use
 #       set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
@@ -36,6 +35,10 @@
 
 if [ -z "$MAVEN_SKIP_RC" ] ; then
 
+  if [ -f /usr/local/etc/mavenrc ] ; then
+    . /usr/local/etc/mavenrc
+  fi
+
   if [ -f /etc/mavenrc ] ; then
     . /etc/mavenrc
   fi
@@ -54,38 +57,16 @@ case "`uname`" in
   CYGWIN*) cygwin=true ;;
   MINGW*) mingw=true;;
   Darwin*) darwin=true
-           #
-           # Look for the Apple JDKs first to preserve the existing behaviour, and then look
-           # for the new JDKs provided by Oracle.
-           # 
-           if [ -z "$JAVA_HOME" ] && [ -L /System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK ] ; then
-             #
-             # Apple JDKs
-             #
-             export JAVA_HOME=/System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK/Home
-           fi
-           
-           if [ -z "$JAVA_HOME" ] && [ -L /System/Library/Java/JavaVirtualMachines/CurrentJDK ] ; then
-             #
-             # Apple JDKs
-             #
-             export JAVA_HOME=/System/Library/Java/JavaVirtualMachines/CurrentJDK/Contents/Home
-           fi
-             
-           if [ -z "$JAVA_HOME" ] && [ -L "/Library/Java/JavaVirtualMachines/CurrentJDK" ] ; then
-             #
-             # Oracle JDKs
-             #
-             export JAVA_HOME=/Library/Java/JavaVirtualMachines/CurrentJDK/Contents/Home
-           fi           
-
-           if [ -z "$JAVA_HOME" ] && [ -x "/usr/libexec/java_home" ]; then
-             #
-             # Apple JDKs
-             #
-             export JAVA_HOME=`/usr/libexec/java_home`
-           fi
-           ;;
+    # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home
+    # See https://developer.apple.com/library/mac/qa/qa1170/_index.html
+    if [ -z "$JAVA_HOME" ]; then
+      if [ -x "/usr/libexec/java_home" ]; then
+        JAVA_HOME="`/usr/libexec/java_home`"; export JAVA_HOME
+      else
+        JAVA_HOME="/Library/Java/Home"; export JAVA_HOME
+      fi
+    fi
+    ;;
 esac
 
 if [ -z "$JAVA_HOME" ] ; then
@@ -94,49 +75,18 @@ if [ -z "$JAVA_HOME" ] ; then
   fi
 fi
 
-if [ -z "$M2_HOME" ] ; then
-  ## resolve links - $0 may be a link to maven's home
-  PRG="$0"
-
-  # need this for relative symlinks
-  while [ -h "$PRG" ] ; do
-    ls=`ls -ld "$PRG"`
-    link=`expr "$ls" : '.*-> \(.*\)$'`
-    if expr "$link" : '/.*' > /dev/null; then
-      PRG="$link"
-    else
-      PRG="`dirname "$PRG"`/$link"
-    fi
-  done
-
-  saveddir=`pwd`
-
-  M2_HOME=`dirname "$PRG"`/..
-
-  # make it fully qualified
-  M2_HOME=`cd "$M2_HOME" && pwd`
-
-  cd "$saveddir"
-  # echo Using m2 at $M2_HOME
-fi
-
 # For Cygwin, ensure paths are in UNIX format before anything is touched
 if $cygwin ; then
-  [ -n "$M2_HOME" ] &&
-    M2_HOME=`cygpath --unix "$M2_HOME"`
   [ -n "$JAVA_HOME" ] &&
     JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
   [ -n "$CLASSPATH" ] &&
     CLASSPATH=`cygpath --path --unix "$CLASSPATH"`
 fi
 
-# For Migwn, ensure paths are in UNIX format before anything is touched
+# For Mingw, ensure paths are in UNIX format before anything is touched
 if $mingw ; then
-  [ -n "$M2_HOME" ] &&
-    M2_HOME="`(cd "$M2_HOME"; pwd)`"
   [ -n "$JAVA_HOME" ] &&
     JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`"
-  # TODO classpath?
 fi
 
 if [ -z "$JAVA_HOME" ]; then
@@ -168,7 +118,7 @@ if [ -z "$JAVACMD" ] ; then
       JAVACMD="$JAVA_HOME/bin/java"
     fi
   else
-    JAVACMD="`which java`"
+    JAVACMD="`\\unset -f command; \\command -v java`"
   fi
 fi
 
@@ -182,31 +132,29 @@ if [ -z "$JAVA_HOME" ] ; then
   echo "Warning: JAVA_HOME environment variable is not set."
 fi
 
-CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher
-
-# For Cygwin, switch paths to Windows format before running java
-if $cygwin; then
-  [ -n "$M2_HOME" ] &&
-    M2_HOME=`cygpath --path --windows "$M2_HOME"`
-  [ -n "$JAVA_HOME" ] &&
-    JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"`
-  [ -n "$CLASSPATH" ] &&
-    CLASSPATH=`cygpath --path --windows "$CLASSPATH"`
-fi
-
 # traverses directory structure from process work directory to filesystem root
 # first directory with .mvn subdirectory is considered project base directory
 find_maven_basedir() {
-  local basedir=$(pwd)
-  local wdir=$(pwd)
+  if [ -z "$1" ]
+  then
+    echo "Path not specified to find_maven_basedir"
+    return 1
+  fi
+
+  basedir="$1"
+  wdir="$1"
   while [ "$wdir" != '/' ] ; do
     if [ -d "$wdir"/.mvn ] ; then
       basedir=$wdir
       break
     fi
-    wdir=$(cd "$wdir/.."; pwd)
+    # workaround for JBEAP-8937 (on Solaris 10/Sparc)
+    if [ -d "${wdir}" ]; then
+      wdir=`cd "$wdir/.."; pwd`
+    fi
+    # end of workaround
   done
-  echo "${basedir}"
+  printf '%s' "$(cd "$basedir"; pwd)"
 }
 
 # concatenates all lines of a file
@@ -216,10 +164,115 @@ concat_lines() {
   fi
 }
 
-export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-$(find_maven_basedir)}
+BASE_DIR=$(find_maven_basedir "$(dirname $0)")
+if [ -z "$BASE_DIR" ]; then
+  exit 1;
+fi
+
+MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}; export MAVEN_PROJECTBASEDIR
+if [ "$MVNW_VERBOSE" = true ]; then
+  echo $MAVEN_PROJECTBASEDIR
+fi
+
+##########################################################################################
+# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
+# This allows using the maven wrapper in projects that prohibit checking in binary data.
+##########################################################################################
+if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then
+    if [ "$MVNW_VERBOSE" = true ]; then
+      echo "Found .mvn/wrapper/maven-wrapper.jar"
+    fi
+else
+    if [ "$MVNW_VERBOSE" = true ]; then
+      echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..."
+    fi
+    if [ -n "$MVNW_REPOURL" ]; then
+      wrapperUrl="$MVNW_REPOURL/org/apache/maven/wrapper/maven-wrapper/3.1.1/maven-wrapper-3.1.1.jar"
+    else
+      wrapperUrl="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.1/maven-wrapper-3.1.1.jar"
+    fi
+    while IFS="=" read key value; do
+      case "$key" in (wrapperUrl) wrapperUrl="$value"; break ;;
+      esac
+    done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties"
+    if [ "$MVNW_VERBOSE" = true ]; then
+      echo "Downloading from: $wrapperUrl"
+    fi
+    wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar"
+    if $cygwin; then
+      wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"`
+    fi
+
+    if command -v wget > /dev/null; then
+        QUIET="--quiet"
+        if [ "$MVNW_VERBOSE" = true ]; then
+          echo "Found wget ... using wget"
+          QUIET=""
+        fi
+        if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
+            wget $QUIET "$wrapperUrl" -O "$wrapperJarPath"
+        else
+            wget $QUIET --http-user="$MVNW_USERNAME" --http-password="$MVNW_PASSWORD" "$wrapperUrl" -O "$wrapperJarPath"
+        fi
+        [ $? -eq 0 ] || rm -f "$wrapperJarPath"
+    elif command -v curl > /dev/null; then
+        QUIET="--silent"
+        if [ "$MVNW_VERBOSE" = true ]; then
+          echo "Found curl ... using curl"
+          QUIET=""
+        fi
+        if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
+            curl $QUIET -o "$wrapperJarPath" "$wrapperUrl" -f -L
+        else
+            curl $QUIET --user "$MVNW_USERNAME:$MVNW_PASSWORD" -o "$wrapperJarPath" "$wrapperUrl" -f -L
+        fi
+        [ $? -eq 0 ] || rm -f "$wrapperJarPath"
+    else
+        if [ "$MVNW_VERBOSE" = true ]; then
+          echo "Falling back to using Java to download"
+        fi
+        javaSource="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java"
+        javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class"
+        # For Cygwin, switch paths to Windows format before running javac
+        if $cygwin; then
+          javaSource=`cygpath --path --windows "$javaSource"`
+          javaClass=`cygpath --path --windows "$javaClass"`
+        fi
+        if [ -e "$javaSource" ]; then
+            if [ ! -e "$javaClass" ]; then
+                if [ "$MVNW_VERBOSE" = true ]; then
+                  echo " - Compiling MavenWrapperDownloader.java ..."
+                fi
+                # Compiling the Java class
+                ("$JAVA_HOME/bin/javac" "$javaSource")
+            fi
+            if [ -e "$javaClass" ]; then
+                # Running the downloader
+                if [ "$MVNW_VERBOSE" = true ]; then
+                  echo " - Running MavenWrapperDownloader.java ..."
+                fi
+                ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR")
+            fi
+        fi
+    fi
+fi
+##########################################################################################
+# End of extension
+##########################################################################################
+
 MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS"
 
-# Provide a "standardized" way to retrieve the CLI args that will 
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin; then
+  [ -n "$JAVA_HOME" ] &&
+    JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"`
+  [ -n "$CLASSPATH" ] &&
+    CLASSPATH=`cygpath --path --windows "$CLASSPATH"`
+  [ -n "$MAVEN_PROJECTBASEDIR" ] &&
+    MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"`
+fi
+
+# Provide a "standardized" way to retrieve the CLI args that will
 # work with both Windows and non-Windows executions.
 MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@"
 export MAVEN_CMD_LINE_ARGS
@@ -228,7 +281,7 @@ WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
 
 exec "$JAVACMD" \
   $MAVEN_OPTS \
+  $MAVEN_DEBUG_OPTS \
   -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \
-  "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
-  ${WRAPPER_LAUNCHER} $MAVEN_CMD_LINE_ARGS
-
+  "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
+  ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@"
diff --git a/mvnw.cmd b/mvnw.cmd
index 001048081..474c9d6b7 100644
--- a/mvnw.cmd
+++ b/mvnw.cmd
@@ -18,15 +18,14 @@
 @REM ----------------------------------------------------------------------------
 
 @REM ----------------------------------------------------------------------------
-@REM Maven2 Start Up Batch script
+@REM Apache Maven Wrapper startup batch script, version 3.1.1
 @REM
 @REM Required ENV vars:
 @REM JAVA_HOME - location of a JDK home dir
 @REM
 @REM Optional ENV vars
-@REM M2_HOME - location of maven2's installed home dir
 @REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands
-@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending
+@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending
 @REM MAVEN_OPTS - parameters passed to the Java VM when running Maven
 @REM     e.g. to debug Maven itself, use
 @REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
@@ -35,7 +34,9 @@
 
 @REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'
 @echo off
-@REM enable echoing my setting MAVEN_BATCH_ECHO to 'on'
+@REM set title of command window
+title %0
+@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on'
 @if "%MAVEN_BATCH_ECHO%" == "on"  echo %MAVEN_BATCH_ECHO%
 
 @REM set %HOME% to equivalent of $HOME
@@ -44,8 +45,8 @@ if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%")
 @REM Execute a user defined script before this one
 if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre
 @REM check for pre script, once with legacy .bat ending and once with .cmd ending
-if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat"
-if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd"
+if exist "%USERPROFILE%\mavenrc_pre.bat" call "%USERPROFILE%\mavenrc_pre.bat" %*
+if exist "%USERPROFILE%\mavenrc_pre.cmd" call "%USERPROFILE%\mavenrc_pre.cmd" %*
 :skipRcPre
 
 @setlocal
@@ -80,8 +81,6 @@ goto error
 
 :init
 
-set MAVEN_CMD_LINE_ARGS=%MAVEN_CONFIG% %*
-
 @REM Find the project base dir, i.e. the directory that contains the folder ".mvn".
 @REM Fallback to current working directory if not found.
 
@@ -117,11 +116,54 @@ for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do s
 :endReadAdditionalConfig
 
 SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe"
-
-set WRAPPER_JAR=""%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar""
+set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar"
 set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
 
-%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CMD_LINE_ARGS%
+set WRAPPER_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.1/maven-wrapper-3.1.1.jar"
+
+FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO (
+    IF "%%A"=="wrapperUrl" SET WRAPPER_URL=%%B
+)
+
+@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
+@REM This allows using the maven wrapper in projects that prohibit checking in binary data.
+if exist %WRAPPER_JAR% (
+    if "%MVNW_VERBOSE%" == "true" (
+        echo Found %WRAPPER_JAR%
+    )
+) else (
+    if not "%MVNW_REPOURL%" == "" (
+        SET WRAPPER_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.1.1/maven-wrapper-3.1.1.jar"
+    )
+    if "%MVNW_VERBOSE%" == "true" (
+        echo Couldn't find %WRAPPER_JAR%, downloading it ...
+        echo Downloading from: %WRAPPER_URL%
+    )
+
+    powershell -Command "&{"^
+		"$webclient = new-object System.Net.WebClient;"^
+		"if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^
+		"$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^
+		"}"^
+		"[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%WRAPPER_URL%', '%WRAPPER_JAR%')"^
+		"}"
+    if "%MVNW_VERBOSE%" == "true" (
+        echo Finished downloading %WRAPPER_JAR%
+    )
+)
+@REM End of extension
+
+@REM Provide a "standardized" way to retrieve the CLI args that will
+@REM work with both Windows and non-Windows executions.
+set MAVEN_CMD_LINE_ARGS=%*
+
+%MAVEN_JAVA_EXE% ^
+  %JVM_CONFIG_MAVEN_PROPS% ^
+  %MAVEN_OPTS% ^
+  %MAVEN_DEBUG_OPTS% ^
+  -classpath %WRAPPER_JAR% ^
+  "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" ^
+  %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %*
 if ERRORLEVEL 1 goto error
 goto end
 
@@ -131,15 +173,15 @@ set ERROR_CODE=1
 :end
 @endlocal & set ERROR_CODE=%ERROR_CODE%
 
-if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost
+if not "%MAVEN_SKIP_RC%"=="" goto skipRcPost
 @REM check for post script, once with legacy .bat ending and once with .cmd ending
-if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat"
-if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd"
+if exist "%USERPROFILE%\mavenrc_post.bat" call "%USERPROFILE%\mavenrc_post.bat"
+if exist "%USERPROFILE%\mavenrc_post.cmd" call "%USERPROFILE%\mavenrc_post.cmd"
 :skipRcPost
 
 @REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'
-if "%MAVEN_BATCH_PAUSE%" == "on" pause
+if "%MAVEN_BATCH_PAUSE%"=="on" pause
 
-if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE%
+if "%MAVEN_TERMINATE_CMD%"=="on" exit %ERROR_CODE%
 
-exit /B %ERROR_CODE%
+cmd /C exit /B %ERROR_CODE%
diff --git a/pom.xml b/pom.xml
index 06f6bfd4b..04acfd2f5 100644
--- a/pom.xml
+++ b/pom.xml
@@ -9,7 +9,7 @@
 
     <groupId>org.openapitools.openapidiff</groupId>
     <artifactId>openapi-diff-parent</artifactId>
-    <version>2.0.1-SNAPSHOT</version>
+    <version>2.1.0-SNAPSHOT</version>
     <packaging>pom</packaging>
 
     <name>openapi-diff-parent</name>
@@ -75,7 +75,7 @@
         <maven.compiler.target>1.8</maven.compiler.target>
         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
         <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
-        <project.build.outputTimestamp>2017-12-12T00:39:30Z</project.build.outputTimestamp>
+        <project.build.outputTimestamp>2025-01-27T23:01:35Z</project.build.outputTimestamp>
         <project.scm.id>github</project.scm.id>
 
         <sonar.organization>openapitools</sonar.organization>
@@ -83,8 +83,8 @@
         <sonar.host.url>https://sonarcloud.io</sonar.host.url>
         <sonar.moduleKey>${project.artifactId}</sonar.moduleKey>
 
-        <swagger-parser.version>2.0.28</swagger-parser.version>
-        <slf4j.version>1.7.32</slf4j.version>
+        <swagger-parser.version>2.1.26</swagger-parser.version>
+        <slf4j.version>2.0.17</slf4j.version>
     </properties>
 
     <dependencyManagement>
@@ -92,12 +92,12 @@
             <dependency>
                 <groupId>org.openapitools.openapidiff</groupId>
                 <artifactId>openapi-diff-core</artifactId>
-                <version>2.0.1-SNAPSHOT</version>
+                <version>2.1.0-SNAPSHOT</version>
             </dependency>
             <dependency>
                 <groupId>org.junit</groupId>
                 <artifactId>junit-bom</artifactId>
-                <version>5.8.2</version>
+                <version>5.12.1</version>
                 <type>pom</type>
                 <scope>import</scope>
             </dependency>
@@ -119,22 +119,27 @@
             <dependency>
                 <groupId>com.j2html</groupId>
                 <artifactId>j2html</artifactId>
-                <version>1.5.0</version>
+                <version>1.6.0</version>
             </dependency>
             <dependency>
                 <groupId>org.apache.commons</groupId>
                 <artifactId>commons-collections4</artifactId>
                 <version>4.4</version>
             </dependency>
+            <dependency>
+                <groupId>org.apache.commons</groupId>
+                <artifactId>commons-configuration2</artifactId>
+                <version>2.11.0</version>
+            </dependency>
             <dependency>
                 <groupId>commons-cli</groupId>
                 <artifactId>commons-cli</artifactId>
-                <version>1.5.0</version>
+                <version>1.9.0</version>
             </dependency>
             <dependency>
                 <groupId>org.apache.commons</groupId>
                 <artifactId>commons-lang3</artifactId>
-                <version>3.12.0</version>
+                <version>3.17.0</version>
             </dependency>
             <dependency>
                 <groupId>org.slf4j</groupId>
@@ -143,24 +148,23 @@
             </dependency>
             <dependency>
                 <groupId>org.slf4j</groupId>
-                <artifactId>slf4j-log4j12</artifactId>
+                <artifactId>jcl-over-slf4j</artifactId>
                 <version>${slf4j.version}</version>
             </dependency>
             <dependency>
-                <groupId>commons-httpclient</groupId>
-                <artifactId>commons-httpclient</artifactId>
-                <version>3.1</version>
+                <groupId>org.slf4j</groupId>
+                <artifactId>slf4j-simple</artifactId>
+                <version>${slf4j.version}</version>
             </dependency>
             <dependency>
-                <groupId>org.assertj</groupId>
-                <artifactId>assertj-core</artifactId>
-                <version>3.21.0</version>
-                <scope>test</scope>
+                <groupId>ch.qos.logback</groupId>
+                <artifactId>logback-classic</artifactId>
+                <version>1.3.14</version>
             </dependency>
             <dependency>
-                <groupId>org.slf4j</groupId>
-                <artifactId>slf4j-simple</artifactId>
-                <version>${slf4j.version}</version>
+                <groupId>org.assertj</groupId>
+                <artifactId>assertj-core</artifactId>
+                <version>3.27.3</version>
                 <scope>test</scope>
             </dependency>
         </dependencies>
@@ -252,6 +256,18 @@
                             </execution>
                         </executions>
                     </plugin>
+                    <plugin>
+                        <groupId>org.cyclonedx</groupId>
+                        <artifactId>cyclonedx-maven-plugin</artifactId>
+                        <executions>
+                            <execution>
+                                <phase>package</phase>
+                                <goals>
+                                    <goal>makeAggregateBom</goal>
+                                </goals>
+                            </execution>
+                        </executions>
+                    </plugin>
                 </plugins>
             </build>
         </profile>
@@ -262,32 +278,39 @@
                 <plugin>
                     <groupId>org.apache.maven.plugins</groupId>
                     <artifactId>maven-compiler-plugin</artifactId>
-                    <version>3.8.1</version>
+                    <version>3.14.0</version>
                 </plugin>
                 <plugin>
                     <groupId>org.apache.maven.plugins</groupId>
                     <artifactId>maven-source-plugin</artifactId>
-                    <version>3.2.1</version>
+                    <version>3.3.1</version>
                 </plugin>
                 <plugin>
                     <groupId>org.apache.maven.plugins</groupId>
                     <artifactId>maven-jar-plugin</artifactId>
-                    <version>3.2.0</version>
+                    <version>3.4.2</version>
+                    <configuration>
+                        <archive>
+                            <manifestEntries>
+                                <Implementation-Version>${project.version}</Implementation-Version>
+                            </manifestEntries>
+                        </archive>
+                    </configuration>
                 </plugin>
                 <plugin>
                     <groupId>org.apache.maven.plugins</groupId>
                     <artifactId>maven-release-plugin</artifactId>
-                    <version>2.5.3</version>
+                    <version>3.1.1</version>
                 </plugin>
                 <plugin>
                     <groupId>org.apache.maven.plugins</groupId>
                     <artifactId>maven-javadoc-plugin</artifactId>
-                    <version>3.3.1</version>
+                    <version>3.11.2</version>
                 </plugin>
                 <plugin>
                     <groupId>org.apache.maven.plugins</groupId>
                     <artifactId>maven-surefire-plugin</artifactId>
-                    <version>2.22.2</version>
+                    <version>3.5.3</version>
                 </plugin>
                 <plugin>
                     <groupId>com.coveo</groupId>
@@ -302,33 +325,38 @@
                 <plugin>
                     <groupId>org.apache.maven.plugins</groupId>
                     <artifactId>maven-gpg-plugin</artifactId>
-                    <version>3.0.1</version>
+                    <version>3.2.7</version>
                 </plugin>
                 <plugin>
                     <groupId>org.apache.maven.plugins</groupId>
                     <artifactId>maven-deploy-plugin</artifactId>
-                    <version>2.8.2</version>
+                    <version>3.1.4</version>
                 </plugin>
                 <plugin>
                     <groupId>org.apache.maven.plugins</groupId>
                     <artifactId>maven-shade-plugin</artifactId>
-                    <version>3.2.4</version>
+                    <version>3.6.0</version>
                 </plugin>
                 <plugin>
                     <groupId>org.sonatype.plugins</groupId>
                     <artifactId>nexus-staging-maven-plugin</artifactId>
-                    <version>1.6.8</version>
+                    <version>1.7.0</version>
                     <extensions>true</extensions>
                 </plugin>
                 <plugin>
                     <groupId>org.jacoco</groupId>
                     <artifactId>jacoco-maven-plugin</artifactId>
-                    <version>0.8.7</version>
+                    <version>0.8.13</version>
                 </plugin>
                 <plugin>
                     <groupId>org.sonarsource.scanner.maven</groupId>
                     <artifactId>sonar-maven-plugin</artifactId>
-                    <version>3.9.1.2184</version>
+                    <version>5.1.0.4751</version>
+                </plugin>
+                <plugin>
+                    <groupId>org.cyclonedx</groupId>
+                    <artifactId>cyclonedx-maven-plugin</artifactId>
+                    <version>2.9.1</version>
                 </plugin>
             </plugins>
         </pluginManagement>
@@ -365,7 +393,7 @@
                             <skipRepositoryCheck>true</skipRepositoryCheck>
                             <hooks>
                                 <pre-commit>
-                                    mvn com.coveo:fmt-maven-plugin:format
+                                    ./mvnw com.coveo:fmt-maven-plugin:format
                                 </pre-commit>
                             </hooks>
                         </configuration>