diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/At.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/At.kt
index 919b13ae3..b044ae4c4 100644
--- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/At.kt
+++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/At.kt
@@ -56,8 +56,18 @@ private constructor(val target: Long, val display: String) :
     }
 
     // 自动为消息补充 " "
+    @Suppress("INAPPLICABLE_JVM_NAME")
+    @Deprecated("for binary compatibility", level = DeprecationLevel.HIDDEN)
+    @JvmName("followedBy")
+    @JvmSynthetic
+    override fun followedBy1(tail: Message): CombinedMessage {
+        if (tail is PlainText && tail.stringValue.startsWith(' ')) {
+            return followedByInternalForBinaryCompatibility(tail)
+        }
+        return followedByInternalForBinaryCompatibility(PlainText(" ") + tail)
+    }
 
-    override fun followedBy(tail: Message): CombinedMessage {
+    override fun followedBy(tail: Message): Message {
         if (tail is PlainText && tail.stringValue.startsWith(' ')) {
             return super.followedBy(tail)
         }
diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/AtAll.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/AtAll.kt
index 36cd382b3..74b4403ca 100644
--- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/AtAll.kt
+++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/AtAll.kt
@@ -15,6 +15,7 @@ package net.mamoe.mirai.message.data
 import net.mamoe.mirai.utils.SinceMirai
 import kotlin.jvm.JvmMultifileClass
 import kotlin.jvm.JvmName
+import kotlin.jvm.JvmSynthetic
 
 private const val displayA = "@全体成员"
 
@@ -41,8 +42,18 @@ object AtAll :
     override fun contentToString(): String = display
 
     // 自动为消息补充 " "
+    @Deprecated("for binary compatibility", level = DeprecationLevel.HIDDEN)
+    @Suppress("INAPPLICABLE_JVM_NAME")
+    @JvmName("followedBy")
+    @JvmSynthetic
+    override fun followedBy1(tail: Message): CombinedMessage {
+        if (tail is PlainText && tail.stringValue.startsWith(' ')) {
+            return followedByInternalForBinaryCompatibility(tail)
+        }
+        return followedByInternalForBinaryCompatibility(PlainText(" ") + tail)
+    }
 
-    override fun followedBy(tail: Message): CombinedMessage {
+    override fun followedBy(tail: Message): Message {
         if (tail is PlainText && tail.stringValue.startsWith(' ')) {
             return super.followedBy(tail)
         }
diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/CombinedMessage.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/CombinedMessage.kt
index 48093df52..e4b96601f 100644
--- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/CombinedMessage.kt
+++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/CombinedMessage.kt
@@ -31,11 +31,12 @@ class CombinedMessage
 @Deprecated(message = "use Message.plus", level = DeprecationLevel.ERROR)
 @MiraiInternalAPI("CombinedMessage 构造器可能会在将来被改动") constructor(
     @MiraiExperimentalAPI("CombinedMessage.left 可能会在将来被改动")
-    val left: Message,
+    val left: SingleMessage,
     @MiraiExperimentalAPI("CombinedMessage.tail 可能会在将来被改动")
-    val tail: Message
-) : Iterable<Message>, Message {
+    val tail: SingleMessage
+) : Iterable<SingleMessage>, Message {
 
+    /*
     // 不要把它用作 local function, 会编译错误
     @OptIn(MiraiExperimentalAPI::class)
     private suspend fun SequenceScope<Message>.yieldCombinedOrElements(message: Message) {
@@ -66,12 +67,15 @@ class CombinedMessage
             }
         }
     }
+    */
 
-    fun asSequence(): Sequence<Message> = sequence {
-        yieldCombinedOrElements(this@CombinedMessage)
+    @OptIn(MiraiExperimentalAPI::class)
+    fun asSequence(): Sequence<SingleMessage> = sequence {
+        yield(left)
+        yield(tail)
     }
 
-    override fun iterator(): Iterator<Message> {
+    override fun iterator(): Iterator<SingleMessage> {
         return asSequence().iterator()
     }
 
@@ -83,9 +87,4 @@ class CombinedMessage
     override fun contentToString(): String {
         return toString()
     }
-
-    @OptIn(MiraiExperimentalAPI::class)
-    fun isFlat(): Boolean {
-        return tail is SingleMessage && left is SingleMessage
-    }
 }
\ No newline at end of file
diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/Message.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/Message.kt
index 9a2222a4c..d27aa10a2 100644
--- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/Message.kt
+++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/Message.kt
@@ -16,6 +16,7 @@ import net.mamoe.mirai.message.MessageReceipt
 import net.mamoe.mirai.utils.MiraiExperimentalAPI
 import net.mamoe.mirai.utils.MiraiInternalAPI
 import net.mamoe.mirai.utils.SinceMirai
+import kotlin.jvm.JvmName
 import kotlin.jvm.JvmSynthetic
 
 /**
@@ -92,6 +93,8 @@ interface Message {
     /**
      * 把 `this` 连接到 [tail] 的头部. 类似于字符串相加.
      *
+     * 连接后无法保证 [ConstrainSingle] 的元素单独存在. 需在
+     *
      * 例:
      * ```kotlin
      * val a = PlainText("Hello ")
@@ -104,16 +107,22 @@ interface Message {
      * println(c) // "Hello world!"
      * ```
      */
+    @SinceMirai("0.34.0")
     @Suppress("DEPRECATION_ERROR")
     @OptIn(MiraiInternalAPI::class)
     @JvmSynthetic // in java they should use `plus` instead
-    fun followedBy(tail: Message): CombinedMessage {
-        if (this is ConstrainSingle<*> && tail is ConstrainSingle<*>
-            && this.key == tail.key
-        ) {
-            return CombinedMessage(EmptyMessageChain, tail)
+    fun followedBy(tail: Message): Message {
+        TODO()
+        if (this is SingleMessage && tail is SingleMessage) {
+            if (this is ConstrainSingle<*> && tail is ConstrainSingle<*>) {
+                return if (this.key == tail.key) {
+                    tail
+                } else {
+                    CombinedMessage(this, tail)
+                }
+            }
+
         }
-        return CombinedMessage(left = this, tail = tail)
     }
 
     /**
@@ -138,15 +147,69 @@ interface Message {
     @SinceMirai("0.34.0")
     fun contentToString(): String
 
-    operator fun plus(another: Message): CombinedMessage = this.followedBy(another)
+    operator fun plus(another: Message): Message = this.followedBy(another)
 
     // avoid resolution ambiguity
-    operator fun plus(another: SingleMessage): CombinedMessage = this.followedBy(another)
+    operator fun plus(another: SingleMessage): Message = this.followedBy(another)
 
-    operator fun plus(another: String): CombinedMessage = this.followedBy(another.toMessage())
+    operator fun plus(another: String): Message = this.followedBy(another.toMessage())
 
     // `+ ""` will be resolved to `plus(String)` instead of `plus(CharSeq)`
-    operator fun plus(another: CharSequence): CombinedMessage = this.followedBy(another.toString().toMessage())
+    operator fun plus(another: CharSequence): Message = this.followedBy(another.toString().toMessage())
+
+
+    // FOR BINARY COMPATIBILITY UNTIL 1.0.0
+
+    @Suppress("INAPPLICABLE_JVM_NAME")
+    @JvmName("followedBy")
+    @Deprecated("for binary compatibility", level = DeprecationLevel.HIDDEN)
+    @JvmSynthetic
+    fun followedBy1(tail: Message): CombinedMessage = this.followedByInternalForBinaryCompatibility(tail)
+
+    @Suppress("INAPPLICABLE_JVM_NAME")
+    @JvmName("plus")
+    @Deprecated("for binary compatibility", level = DeprecationLevel.HIDDEN)
+    @JvmSynthetic
+    fun plus1(another: Message): CombinedMessage = this.followedByInternalForBinaryCompatibility(another)
+
+    @Suppress("INAPPLICABLE_JVM_NAME")
+    @JvmName("plus")
+    @Deprecated("for binary compatibility", level = DeprecationLevel.HIDDEN)
+    @JvmSynthetic
+    fun plus1(another: SingleMessage): CombinedMessage = this.followedByInternalForBinaryCompatibility(another)
+
+    @Suppress("INAPPLICABLE_JVM_NAME")
+    @JvmName("plus")
+    @Deprecated("for binary compatibility", level = DeprecationLevel.HIDDEN)
+    @JvmSynthetic
+    fun plus1(another: String): CombinedMessage = this.followedByInternalForBinaryCompatibility(another.toMessage())
+
+    @Suppress("INAPPLICABLE_JVM_NAME")
+    @JvmName("plus")
+    @Deprecated("for binary compatibility", level = DeprecationLevel.HIDDEN)
+    @JvmSynthetic
+    fun plus1(another: CharSequence): CombinedMessage =
+        this.followedByInternalForBinaryCompatibility(another.toString().toMessage())
+}
+
+@OptIn(MiraiInternalAPI::class)
+@JvmSynthetic
+@Suppress("DEPRECATION_ERROR")
+internal fun Message.followedByInternalForBinaryCompatibility(tail: Message): CombinedMessage {
+    TODO()
+    if (this is ConstrainSingle<*>) {
+
+    }
+
+    if (this is SingleMessage && tail is SingleMessage) {
+        if (this is ConstrainSingle<*> && tail is ConstrainSingle<*>
+            && this.key == tail.key
+        ) return CombinedMessage(EmptyMessageChain, tail)
+        return CombinedMessage(left = this, tail = tail)
+    } else {
+
+        //    return CombinedMessage(left = this.constrain)
+    }
 }
 
 @JvmSynthetic
diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/MessageChain.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/MessageChain.kt
index 342ca1f36..518d4a100 100644
--- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/MessageChain.kt
+++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/MessageChain.kt
@@ -387,10 +387,8 @@ fun Message.flatten(): Sequence<SingleMessage> {
 @JvmSynthetic // make Java user happier with less methods
 fun CombinedMessage.flatten(): Sequence<SingleMessage> {
     // already constrained single.
-    if (this.isFlat()) {
-        @Suppress("UNCHECKED_CAST")
-        return (this as Iterable<SingleMessage>).asSequence()
-    } else return this.asSequence().flatten()
+    @Suppress("UNCHECKED_CAST")
+    return (this as Iterable<SingleMessage>).asSequence()
 }
 
 @JvmSynthetic // make Java user happier with less methods
@@ -402,11 +400,17 @@ inline fun MessageChain.flatten(): Sequence<SingleMessage> = this.asSequence() /
 /**
  * 不含任何元素的 [MessageChain]
  */
-object EmptyMessageChain : MessageChain, Iterator<SingleMessage> {
+object EmptyMessageChain : MessageChain, Iterator<SingleMessage>, @MiraiExperimentalAPI SingleMessage {
     override fun contains(sub: String): Boolean = sub.isEmpty()
     override val size: Int get() = 0
     override fun toString(): String = ""
     override fun contentToString(): String = ""
+
+    override val length: Int get() = 0
+    override fun get(index: Int): Char = ""[index]
+    override fun subSequence(startIndex: Int, endIndex: Int): CharSequence = "".subSequence(startIndex, endIndex)
+    override fun compareTo(other: String): Int = "".compareTo(other)
+
     override fun iterator(): Iterator<SingleMessage> = this
     override fun hasNext(): Boolean = false
     override fun next(): SingleMessage = throw NoSuchElementException("EmptyMessageChain is empty.")
@@ -424,10 +428,6 @@ object NullMessageChain : MessageChain {
     override val size: Int get() = 0
     override fun equals(other: Any?): Boolean = other === this
     override fun contains(sub: String): Boolean = error("accessing NullMessageChain")
-
-    @OptIn(MiraiInternalAPI::class)
-    @Suppress("DEPRECATION_ERROR")
-    override fun followedBy(tail: Message): CombinedMessage = error("accessing NullMessageChain")
     override fun iterator(): MutableIterator<SingleMessage> = error("accessing NullMessageChain")
 }
 
diff --git a/mirai-core/src/commonTest/kotlin/net/mamoe/mirai/message.data/ConstrainSingleTest.kt b/mirai-core/src/commonTest/kotlin/net/mamoe/mirai/message.data/ConstrainSingleTest.kt
index 4bb537f6b..0d88c51f0 100644
--- a/mirai-core/src/commonTest/kotlin/net/mamoe/mirai/message.data/ConstrainSingleTest.kt
+++ b/mirai-core/src/commonTest/kotlin/net/mamoe/mirai/message.data/ConstrainSingleTest.kt
@@ -52,7 +52,7 @@ internal class ConstrainSingleTest {
     @Test
     fun testConstrainSingleInPlus() {
         val new = TestConstrainSingleMessage()
-        val combined = TestConstrainSingleMessage() + new
+        val combined = (TestConstrainSingleMessage() + new) as CombinedMessage
 
         assertEquals(combined.left, EmptyMessageChain)
         assertSame(combined.tail, new)